1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if_arp.h>
4 
5 #include "sd-netlink.h"
6 
7 #include "netlink-util.h"
8 #include "networkd-manager.h"
9 #include "networkd-wiphy.h"
10 #include "parse-util.h"
11 #include "wifi-util.h"
12 #include "wlan.h"
13 
wlan_done(NetDev * netdev)14 static void wlan_done(NetDev *netdev) {
15         WLan *w;
16 
17         assert(netdev);
18 
19         w = WLAN(netdev);
20 
21         assert(w);
22 
23         w->wiphy_name = mfree(w->wiphy_name);
24 }
25 
wlan_init(NetDev * netdev)26 static void wlan_init(NetDev *netdev) {
27         WLan *w;
28 
29         assert(netdev);
30 
31         w = WLAN(netdev);
32 
33         assert(w);
34 
35         w->wiphy_index = UINT32_MAX;
36         w->wds = -1;
37 }
38 
wlan_get_wiphy(NetDev * netdev,Wiphy ** ret)39 static int wlan_get_wiphy(NetDev *netdev, Wiphy **ret) {
40         WLan *w;
41 
42         assert(netdev);
43 
44         w = WLAN(netdev);
45 
46         assert(w);
47 
48         if (w->wiphy_name)
49                 return wiphy_get_by_name(netdev->manager, w->wiphy_name, ret);
50 
51         return wiphy_get_by_index(netdev->manager, w->wiphy_index, ret);
52 }
53 
wlan_is_ready_to_create(NetDev * netdev,Link * link)54 static int wlan_is_ready_to_create(NetDev *netdev, Link *link) {
55         return wlan_get_wiphy(netdev, NULL) >= 0;
56 }
57 
wlan_fill_message(NetDev * netdev,sd_netlink_message * m)58 static int wlan_fill_message(NetDev *netdev, sd_netlink_message *m) {
59         Wiphy *wiphy;
60         WLan *w;
61         int r;
62 
63         assert(netdev);
64         assert(m);
65 
66         w = WLAN(netdev);
67 
68         assert(w);
69 
70         r = wlan_get_wiphy(netdev, &wiphy);
71         if (r < 0)
72                 return r;
73 
74         r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, wiphy->index);
75         if (r < 0)
76                 return r;
77 
78         r = sd_netlink_message_append_string(m, NL80211_ATTR_IFNAME, netdev->ifname);
79         if (r < 0)
80                 return r;
81 
82         r = sd_netlink_message_append_u32(m, NL80211_ATTR_IFTYPE, w->iftype);
83         if (r < 0)
84                 return r;
85 
86         if (!hw_addr_is_null(&netdev->hw_addr) && netdev->hw_addr.length == ETH_ALEN) {
87                 r = sd_netlink_message_append_ether_addr(m, NL80211_ATTR_MAC, &netdev->hw_addr.ether);
88                 if (r < 0)
89                         return r;
90         }
91 
92         if (w->wds >= 0) {
93                 r = sd_netlink_message_append_u8(m, NL80211_ATTR_4ADDR, w->wds);
94                 if (r < 0)
95                         return r;
96         }
97 
98         return 0;
99 }
100 
wlan_create_handler(sd_netlink * genl,sd_netlink_message * m,NetDev * netdev)101 static int wlan_create_handler(sd_netlink *genl, sd_netlink_message *m, NetDev *netdev) {
102         int r;
103 
104         assert(netdev);
105         assert(netdev->state != _NETDEV_STATE_INVALID);
106 
107         r = sd_netlink_message_get_errno(m);
108         if (IN_SET(r, -EEXIST, -ENFILE))
109                 /* Unlike the other netdevs, the kernel may return -ENFILE. See dev_alloc_name(). */
110                 log_netdev_info(netdev, "WLAN interface exists, using existing without changing its parameters.");
111         else if (r < 0) {
112                 log_netdev_warning_errno(netdev, r, "WLAN interface could not be created: %m");
113                 netdev_enter_failed(netdev);
114 
115                 return 1;
116         }
117 
118         log_netdev_debug(netdev, "WLAN interface is created.");
119         return 1;
120 }
121 
wlan_create(NetDev * netdev)122 static int wlan_create(NetDev *netdev) {
123         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
124         int r;
125 
126         assert(netdev);
127         assert(netdev->manager);
128         assert(netdev->manager->genl);
129 
130         r = sd_genl_message_new(netdev->manager->genl, NL80211_GENL_NAME, NL80211_CMD_NEW_INTERFACE, &m);
131         if (r < 0)
132                 return log_netdev_warning_errno(netdev, r, "Failed to allocate netlink message: %m");
133 
134         r = wlan_fill_message(netdev, m);
135         if (r < 0)
136                 return log_netdev_warning_errno(netdev, r, "Failed to fill netlink message: %m");
137 
138         r = netlink_call_async(netdev->manager->genl, NULL, m, wlan_create_handler,
139                                netdev_destroy_callback, netdev);
140         if (r < 0)
141                 return log_netdev_warning_errno(netdev, r, "Failed to send netlink message: %m");
142 
143         netdev_ref(netdev);
144         return 0;
145 }
146 
wlan_verify(NetDev * netdev,const char * filename)147 static int wlan_verify(NetDev *netdev, const char *filename) {
148         WLan *w;
149 
150         assert(netdev);
151         assert(filename);
152 
153         w = WLAN(netdev);
154 
155         assert(w);
156 
157         if (w->iftype == NL80211_IFTYPE_UNSPECIFIED)
158                 return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
159                                                 "%s: WLAN interface type is not specified, ignoring.",
160                                                 filename);
161 
162         if (w->wiphy_index == UINT32_MAX && isempty(w->wiphy_name))
163                 return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
164                                                 "%s: physical WLAN device is not specified, ignoring.",
165                                                 filename);
166 
167         return 0;
168 }
169 
config_parse_wiphy(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)170 int config_parse_wiphy(
171                 const char *unit,
172                 const char *filename,
173                 unsigned line,
174                 const char *section,
175                 unsigned section_line,
176                 const char *lvalue,
177                 int ltype,
178                 const char *rvalue,
179                 void *data,
180                 void *userdata) {
181 
182         WLan *w = userdata;
183         int r;
184 
185         assert(filename);
186         assert(lvalue);
187         assert(rvalue);
188         assert(userdata);
189 
190         if (isempty(rvalue)) {
191                 w->wiphy_name = mfree(w->wiphy_name);
192                 w->wiphy_index = UINT32_MAX;
193                 return 0;
194         }
195 
196         r = safe_atou32(rvalue, &w->wiphy_index);
197         if (r >= 0) {
198                 w->wiphy_name = mfree(w->wiphy_name);
199                 return 0;
200         }
201 
202         r = free_and_strdup_warn(&w->wiphy_name, rvalue);
203         if (r < 0)
204                 return r;
205 
206         w->wiphy_index = UINT32_MAX;
207         return 0;
208 }
209 
config_parse_wlan_iftype(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)210 int config_parse_wlan_iftype(
211                 const char *unit,
212                 const char *filename,
213                 unsigned line,
214                 const char *section,
215                 unsigned section_line,
216                 const char *lvalue,
217                 int ltype,
218                 const char *rvalue,
219                 void *data,
220                 void *userdata) {
221 
222         enum nl80211_iftype t, *iftype = data;
223 
224         assert(filename);
225         assert(lvalue);
226         assert(rvalue);
227         assert(data);
228 
229         if (isempty(rvalue)) {
230                 *iftype = NL80211_IFTYPE_UNSPECIFIED;
231                 return 0;
232         }
233 
234         t = nl80211_iftype_from_string(rvalue);
235         /* We reuse the kernel provided enum which does not contain negative value. So, the cast
236          * below is mandatory. Otherwise, the check below always passes. */
237         if ((int) t < 0) {
238                 log_syntax(unit, LOG_WARNING, filename, line, t,
239                            "Failed to parse wlan interface type, ignoring assignment: %s",
240                            rvalue);
241                 return 0;
242         }
243 
244         *iftype = t;
245         return 0;
246 }
247 
248 const NetDevVTable wlan_vtable = {
249         .object_size = sizeof(WLan),
250         .init = wlan_init,
251         .done = wlan_done,
252         .sections = NETDEV_COMMON_SECTIONS "WLAN\0",
253         .is_ready_to_create = wlan_is_ready_to_create,
254         .create = wlan_create,
255         .create_type = NETDEV_CREATE_INDEPENDENT,
256         .config_verify = wlan_verify,
257         .iftype = ARPHRD_ETHER,
258         .generate_mac = true,
259         .skip_netdev_kind_check = true,
260 };
261