discover: Add network handling
[petitboot] / discover / network.c
1
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <sys/socket.h>
7 #include <linux/if.h>
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10
11 #include <log/log.h>
12 #include <list/list.h>
13 #include <talloc/talloc.h>
14 #include <waiter/waiter.h>
15 #include <pb-config/pb-config.h>
16 #include <system/system.h>
17
18 #include "network.h"
19
20 #define HWADDR_SIZE     6
21 #define PIDFILE_BASE    (LOCAL_STATE_DIR "/petitboot/")
22
23 #define for_each_nlmsg(buf, nlmsg, len) \
24         for (nlmsg = (struct nlmsghdr *)buf; \
25                 NLMSG_OK(nlmsg, len) && nlmsg->nlmsg_type != NLMSG_DONE; \
26                 nlmsg = NLMSG_NEXT(nlmsg, len))
27
28 #define for_each_rta(buf, rta, attrlen) \
29         for (rta = (struct rtattr *)(buf); RTA_OK(rta, attrlen); \
30                         rta = RTA_NEXT(rta, attrlen))
31
32
33 struct interface {
34         int     ifindex;
35         char    name[IFNAMSIZ];
36         uint8_t hwaddr[HWADDR_SIZE];
37
38         enum {
39                 IFSTATE_NEW,
40                 IFSTATE_UP_WAITING_LINK,
41                 IFSTATE_CONFIGURED,
42                 IFSTATE_IGNORED,
43         } state;
44
45         struct list_item list;
46 };
47
48 struct network {
49         struct list     interfaces;
50         struct waiter   *waiter;
51         int             netlink_sd;
52         bool            manual_config;
53         bool            dry_run;
54 };
55
56 static const struct network_config *find_config_by_hwaddr(
57                 uint8_t *hwaddr)
58 {
59         const struct config *config;
60         int i;
61
62         config = config_get();
63         if (!config)
64                 return NULL;
65
66         for (i = 0; i < config->n_network_configs; i++) {
67                 struct network_config *netconf = config->network_configs[i];
68
69                 if (!memcmp(netconf->hwaddr, hwaddr, HWADDR_SIZE))
70                         return netconf;
71         }
72
73         return NULL;
74 }
75
76 static struct interface *find_interface_by_ifindex(struct network *network,
77                 int ifindex)
78 {
79         struct interface *interface;
80
81         list_for_each_entry(&network->interfaces, interface, list)
82                 if (interface->ifindex == ifindex)
83                         return interface;
84
85         return NULL;
86 }
87
88 static int network_init_netlink(struct network *network)
89 {
90         struct sockaddr_nl addr;
91         int rc;
92
93         memset(&addr, 0, sizeof(addr));
94         addr.nl_family = AF_NETLINK;
95         addr.nl_groups = RTMGRP_LINK;
96
97         network->netlink_sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
98         if (network->netlink_sd < 0) {
99                 perror("socket(AF_NETLINK)");
100                 return -1;
101         }
102
103         rc = bind(network->netlink_sd, (struct sockaddr *)&addr, sizeof(addr));
104         if (rc) {
105                 perror("bind(sockaddr_nl)");
106                 close(network->netlink_sd);
107                 return -1;
108         }
109
110         return 0;
111 }
112
113 static int network_send_link_query(struct network *network)
114 {
115         int rc;
116         struct {
117                 struct nlmsghdr nlmsg;
118                 struct rtgenmsg rtmsg;
119         } msg;
120
121         memset(&msg, 0, sizeof(msg));
122
123         msg.nlmsg.nlmsg_len = sizeof(msg);
124         msg.nlmsg.nlmsg_type = RTM_GETLINK;
125         msg.nlmsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
126         msg.nlmsg.nlmsg_seq = 0;
127         msg.nlmsg.nlmsg_pid = 0;
128         msg.rtmsg.rtgen_family = AF_UNSPEC;
129
130         rc = send(network->netlink_sd, &msg, sizeof(msg), MSG_NOSIGNAL);
131         if (rc != sizeof(msg))
132                 return -1;
133
134         return 0;
135 }
136
137 static int interface_up(struct network *network, struct interface *interface)
138 {
139         int rc;
140         const char *argv[] = {
141                 pb_system_apps.ip,
142                 "link",
143                 "set",
144                 interface->name,
145                 "up",
146                 NULL,
147         };
148
149         rc = pb_run_cmd(argv, 1, network->dry_run);
150         if (rc) {
151                 pb_log("failed to bring interface %s up\n", interface->name);
152                 return -1;
153         }
154         return 0;
155 }
156
157 static void configure_interface_dhcp(struct network *network,
158                 struct interface *interface)
159 {
160         char pidfile[256];
161         const char *argv[] = {
162                 pb_system_apps.udhcpc,
163                 "-R",
164                 "-n",
165                 "-p", pidfile,
166                 "-i", interface->name,
167                 NULL,
168         };
169         snprintf(pidfile, sizeof(pidfile), "%s/udhcpc-%s.pid",
170                         PIDFILE_BASE, interface->name);
171
172         pb_run_cmd(argv, 0, network->dry_run);
173         return;
174 }
175
176 static void configure_interface_static(struct network *network,
177                 struct interface *interface,
178                 const struct network_config *config)
179 {
180         const char *addr_argv[] = {
181                 pb_system_apps.ip,
182                 "address",
183                 "add",
184                 config->static_config.address,
185                 "dev",
186                 interface->name,
187                 NULL,
188         };
189         const char *route_argv[] = {
190                 pb_system_apps.ip,
191                 "route",
192                 "add",
193                 "default",
194                 "via",
195                 config->static_config.gateway,
196                 NULL,
197         };
198         int rc;
199
200
201         rc = pb_run_cmd(addr_argv, 1, network->dry_run);
202         if (rc) {
203                 pb_log("failed to add address %s to interface %s\n",
204                                 config->static_config.address,
205                                 interface->name);
206                 return;
207         }
208
209         /* we need the interface up before we can route through it */
210         rc = interface_up(network, interface);
211         if (rc)
212                 return;
213
214         if (config->static_config.gateway)
215                 rc = pb_run_cmd(route_argv, 1, network->dry_run);
216
217         if (rc) {
218                 pb_log("failed to add default route %s on interface %s\n",
219                                 config->static_config.gateway,
220                                 interface->name);
221         }
222
223         return;
224 }
225
226 static void configure_interface(struct network *network,
227                 struct interface *interface, bool up, bool link)
228 {
229         const struct network_config *config = NULL;
230
231         if (interface->state == IFSTATE_IGNORED)
232                 return;
233
234         /* old interface? check that we're still up and running */
235         if (interface->state == IFSTATE_CONFIGURED) {
236                 if (!up)
237                         interface->state = IFSTATE_NEW;
238                 else if (!link)
239                         interface->state = IFSTATE_UP_WAITING_LINK;
240                 else
241                         return;
242         }
243
244         /* always up the lookback, no other handling required */
245         if (!strcmp(interface->name, "lo")) {
246                 if (interface->state == IFSTATE_NEW)
247                         interface_up(network, interface);
248                 interface->state = IFSTATE_CONFIGURED;
249                 return;
250         }
251
252         config = find_config_by_hwaddr(interface->hwaddr);
253         if (config && config->ignore) {
254                 pb_log("network: ignoring interface %s\n", interface->name);
255                 interface->state = IFSTATE_IGNORED;
256                 return;
257         }
258
259         /* if we're in manual config mode, we need an interface configuration */
260         if (network->manual_config && !config) {
261                 interface->state = IFSTATE_IGNORED;
262                 pb_log("network: skipping %s: manual config mode, "
263                                 "but no config for this interface\n",
264                                 interface->name);
265                 return;
266         }
267
268         /* new interface? bring up to the point so we can detect a link */
269         if (interface->state == IFSTATE_NEW) {
270                 if (!up) {
271                         interface_up(network, interface);
272                         pb_log("network: bringing up interface %s\n",
273                                         interface->name);
274                         return;
275
276                 } else if (!link) {
277                         interface->state = IFSTATE_UP_WAITING_LINK;
278                 }
279         }
280
281         /* no link? wait for a notification */
282         if (interface->state == IFSTATE_UP_WAITING_LINK && !link)
283                 return;
284
285         pb_log("network: configuring interface %s\n", interface->name);
286
287         if (!config || config->method == CONFIG_METHOD_DHCP) {
288                 configure_interface_dhcp(network, interface);
289
290         } else if (config->method == CONFIG_METHOD_STATIC) {
291                 configure_interface_static(network, interface, config);
292         }
293 }
294
295 static int network_handle_nlmsg(struct network *network, struct nlmsghdr *nlmsg)
296 {
297         bool have_ifaddr, have_ifname;
298         struct interface *interface;
299         struct ifinfomsg *info;
300         struct rtattr *attr;
301         uint8_t ifaddr[6];
302         char ifname[IFNAMSIZ+1];
303         int attrlen, type;
304
305
306         /* we're only interested in NEWLINK messages */
307         type = nlmsg->nlmsg_type;
308         if (!(type == RTM_NEWLINK || type == RTM_DELLINK))
309                 return 0;
310
311         info = NLMSG_DATA(nlmsg);
312
313         have_ifaddr = have_ifname = false;
314
315         attrlen = nlmsg->nlmsg_len - sizeof(*info);
316
317         /* extract the interface name and hardware address attributes */
318         for_each_rta(info + 1, attr, attrlen) {
319                 void *data = RTA_DATA(attr);
320
321                 switch (attr->rta_type) {
322                 case IFLA_ADDRESS:
323                         memcpy(ifaddr, data, sizeof(ifaddr));
324                         have_ifaddr = true;
325                         break;
326
327                 case IFLA_IFNAME:
328                         strncpy(ifname, data, IFNAMSIZ);
329                         have_ifname = true;
330                         break;
331                 }
332         }
333
334         if (!have_ifaddr || !have_ifname)
335                 return -1;
336
337         if (type == RTM_DELLINK) {
338                 interface = find_interface_by_ifindex(network, info->ifi_index);
339                 if (!interface)
340                         return 0;
341                 pb_log("network: interface %s removed\n", interface->name);
342                 list_remove(&interface->list);
343                 talloc_free(interface);
344                 return 0;
345         }
346
347
348         interface = find_interface_by_ifindex(network, info->ifi_index);
349         if (!interface) {
350                 interface = talloc_zero(network, struct interface);
351                 interface->ifindex = info->ifi_index;
352                 interface->state = IFSTATE_NEW;
353                 memcpy(interface->hwaddr, ifaddr, sizeof(interface->hwaddr));
354                 strncpy(interface->name, ifname, sizeof(interface->name) - 1);
355         }
356
357         configure_interface(network, interface,
358                         info->ifi_flags & IFF_UP,
359                         info->ifi_flags & IFF_LOWER_UP);
360
361         return 0;
362 }
363
364 static int network_netlink_process(void *arg)
365 {
366         struct network *network = arg;
367         struct nlmsghdr *nlmsg;
368         unsigned int len;
369         char buf[4096];
370         int rc;
371
372         rc = recv(network->netlink_sd, buf, sizeof(buf), 0);
373         if (rc < 0) {
374                 perror("netlink recv");
375                 return -1;
376         }
377
378         len = rc;
379
380         for_each_nlmsg(buf, nlmsg, len)
381                 network_handle_nlmsg(network, nlmsg);
382
383         return 0;
384 }
385
386 struct network *network_init(void *ctx, struct waitset *waitset, bool dry_run)
387 {
388         struct network *network;
389         int rc;
390
391         network = talloc(ctx, struct network);
392         list_init(&network->interfaces);
393         network->manual_config = false;
394         network->dry_run = dry_run;
395
396         rc = network_init_netlink(network);
397         if (rc)
398                 goto err;
399
400         network->waiter = waiter_register_io(waitset, network->netlink_sd,
401                         WAIT_IN, network_netlink_process, network);
402
403         if (!network->waiter)
404                 goto err;
405
406         rc = network_send_link_query(network);
407         if (rc)
408                 goto err;
409
410         return network;
411
412 err:
413         network_shutdown(network);
414         return NULL;
415 }
416
417
418 int network_shutdown(struct network *network)
419 {
420         if (network->waiter)
421                 waiter_remove(network->waiter);
422
423         close(network->netlink_sd);
424         talloc_free(network);
425         return 0;
426 }