1 #include <nm_core.h>
2 #include <nm_utils.h>
3 #include <nm_network.h>
4 
5 #include <sys/ioctl.h>
6 
7 #if defined (NM_OS_LINUX)
8 
9 #ifdef _DEFAULT_SOURCE
10 #include <net/if.h>
11 #include <linux/if.h>
12 #else
13 /* Temporary work-around for broken glibc vs. linux kernel header definitions
14  * This is already fixed upstream, remove this when distributions have updated.
15  * net/if.h fuckup should be removed someday in future, when kernels <= 4.2 will not be supported
16  * https://github.com/systemd/systemd/commit/08ce521fb2546921f2642bef067d2cc02158b121
17  * https://github.com/systemd/systemd/commit/6f270e6bd8b78aedf9f77534d6d11141ea0bf8ca
18  */
19 #define _NET_IF_H 1
20 #include <net/if.h>
21 #ifndef IFNAMSIZ
22 #define IFNAMSIZ 16
23 extern unsigned int if_nametoindex (const char *__ifname) __THROW;
24 #endif
25 #include <linux/if.h>
26 #endif
27 
28 #include <time.h>
29 #include <sys/socket.h>
30 #include <linux/if_tun.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
33 
34 static const char NM_TUNDEV[]      = "/dev/net/tun";
35 static const char NM_NET_MACVTAP[] = "macvtap";
36 static const char NM_NET_VETH[]    = "veth";
37 static const int NM_NET_VETH_INFO_PEER = 1;
38 
39 enum {
40     NM_MACVTAP_BRIDGE = 1,
41     NM_MACVTAP_PRIVATE = 2
42 };
43 
44 enum {
45     NM_MACVTAP_PRIVATE_MODE = 1,
46     NM_MACVTAP_BRIDGE_MODE = 4
47 };
48 
49 struct iplink_req {
50     struct nlmsghdr n;
51     struct ifinfomsg i;
52     /* cppcheck-suppress unusedStructMember */
53     char buf[1024];
54 };
55 
56 struct ipaddr_req {
57     struct nlmsghdr n;
58     struct ifaddrmsg i;
59     /* cppcheck-suppress unusedStructMember */
60     char buf[256];
61 };
62 
63 struct rtnl_handle {
64     int32_t sd;
65     uint32_t seq;
66     struct sockaddr_nl sa;
67 };
68 
69 static void nm_net_set_link_status(const nm_str_t *name, int action);
70 static void nm_net_rtnl_open(struct rtnl_handle *rth);
71 static void nm_net_rtnl_talk(struct rtnl_handle *rth, struct nlmsghdr *n,
72                              struct nlmsghdr *res, size_t res_len);
73 static int nm_net_add_attr(struct nlmsghdr *n, size_t mlen,
74                            int type, const void *data, size_t dlen);
75 static struct rtattr *nm_net_add_attr_nest(struct nlmsghdr *n, size_t mlen,
76                                            int type);
77 static int nm_net_add_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
78 
NLMSG_TAIL(struct nlmsghdr * n)79 static struct rtattr *NLMSG_TAIL(struct nlmsghdr* n)
80 {
81     return (struct rtattr *)((char *)n + NLMSG_ALIGN(n->nlmsg_len));
82 }
83 #else
84 #include <net/if.h>
85 #endif /* NM_OS_LINUX */
86 
87 enum tap_on_off {
88     NM_TAP_OFF = 0,
89     NM_TAP_ON
90 };
91 
92 enum action {
93     NM_SET_LINK_UP,
94     NM_SET_LINK_DOWN,
95     NM_SET_LINK_ADDR
96 };
97 
98 static size_t nm_net_mac_s2a(const nm_str_t *addr, char *res, size_t len);
99 static void nm_net_manage_tap(const nm_str_t *name, int on_off);
100 static void nm_net_addr_change(const nm_str_t *name, const nm_str_t *net,
101                                int action);
102 
nm_net_iface_exists(const nm_str_t * name)103 int nm_net_iface_exists(const nm_str_t *name)
104 {
105     if (if_nametoindex(name->data) == 0)
106         return NM_ERR;
107 
108     return NM_OK;
109 }
110 
nm_net_iface_idx(const nm_str_t * name)111 uint32_t nm_net_iface_idx(const nm_str_t *name)
112 {
113     return if_nametoindex(name->data);
114 }
115 
nm_net_add_tap(const nm_str_t * name)116 void nm_net_add_tap(const nm_str_t *name)
117 {
118     nm_net_manage_tap(name, NM_TAP_ON);
119 }
120 
nm_net_del_tap(const nm_str_t * name)121 void nm_net_del_tap(const nm_str_t *name)
122 {
123     nm_net_manage_tap(name, NM_TAP_OFF);
124 }
125 
126 #if defined (NM_OS_LINUX)
nm_net_add_macvtap(const nm_str_t * name,const nm_str_t * parent,const nm_str_t * maddr,int type)127 void nm_net_add_macvtap(const nm_str_t *name, const nm_str_t *parent,
128                         const nm_str_t *maddr, int type)
129 {
130     struct iplink_req req;
131     struct rtnl_handle rth;
132     struct rtattr *linkinfo, *data;
133     uint32_t dev_index, mode = 0;
134     size_t mac_len;
135     char macn[32] = {0};
136 
137     memset(&req, 0, sizeof(req));
138 
139     if ((dev_index = if_nametoindex(parent->data)) == 0)
140         nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
141 
142     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
143     req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
144     req.n.nlmsg_type = RTM_NEWLINK;
145 
146     req.i.ifi_family = AF_UNSPEC;
147     req.i.ifi_flags |= IFF_UP | IFF_MULTICAST | IFF_ALLMULTI;
148 
149     mac_len = nm_net_mac_s2a(maddr, macn, sizeof(macn));
150     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_ADDRESS,
151             macn, mac_len) != NM_OK)) {
152         nm_bug("%s: Error add_attr", __func__);
153     }
154 
155     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_LINK,
156             &dev_index, sizeof(dev_index)) != NM_OK)) {
157         nm_bug("%s: Error add_attr", __func__);
158     }
159 
160     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
161             name->data, name->len) != NM_OK)) {
162         nm_bug("%s: Error add_attr", __func__);
163     }
164 
165     linkinfo = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
166     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_INFO_KIND,
167             NM_NET_MACVTAP, strlen(NM_NET_MACVTAP)) != NM_OK)) {
168         nm_bug("%s: Error add_attr", __func__);
169     }
170 
171     switch (type) {
172     case NM_MACVTAP_BRIDGE:
173         mode = NM_MACVTAP_BRIDGE_MODE;
174         break;
175     case NM_MACVTAP_PRIVATE:
176         mode = NM_MACVTAP_PRIVATE_MODE;
177         break;
178     }
179 
180     data = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_INFO_DATA);
181     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_MACVLAN_MODE,
182             &mode, sizeof(mode)) != NM_OK)) {
183         nm_bug("%s: Error add_attr", __func__);
184     }
185 
186     nm_net_add_attr_nest_end(&req.n, data);
187     nm_net_add_attr_nest_end(&req.n, linkinfo);
188 
189     nm_net_rtnl_open(&rth);
190     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
191     close(rth.sd);
192 }
193 
nm_net_add_veth(const nm_str_t * l_name,const nm_str_t * r_name)194 void nm_net_add_veth(const nm_str_t *l_name, const nm_str_t *r_name)
195 {
196     struct iplink_req req;
197     struct rtnl_handle rth;
198     struct rtattr *linkinfo, *data;
199 
200     memset(&req, 0, sizeof(req));
201 
202     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
203     req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
204     req.n.nlmsg_type = RTM_NEWLINK;
205 
206     req.i.ifi_family = AF_UNSPEC;
207     req.i.ifi_index = 0;
208 
209     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
210             l_name->data, l_name->len) != NM_OK)) {
211         nm_bug("%s: Error add_attr", __func__);
212     }
213 
214     linkinfo = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
215     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_INFO_KIND,
216             NM_NET_VETH, strlen(NM_NET_VETH)) != NM_OK)) {
217         nm_bug("%s: Error add_attr", __func__);
218     }
219 
220     data = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_INFO_DATA);
221 
222     {
223         struct rtattr *vdata;
224         uint32_t ifi_flags, ifi_change;
225         struct ifinfomsg *ifm, *peer_ifm;
226 
227         ifm = NLMSG_DATA(&req.n);
228         ifi_flags = ifm->ifi_flags;
229         ifi_change = ifm->ifi_change;
230         ifm->ifi_flags = 0;
231         ifm->ifi_change = 0;
232 
233         vdata = NLMSG_TAIL(&req.n);
234         if ((nm_net_add_attr(&req.n, sizeof(req), NM_NET_VETH_INFO_PEER,
235                 NULL, 0) != NM_OK)) {
236             nm_bug("%s: Error add_attr", __func__);
237         }
238 
239         req.n.nlmsg_len += sizeof(struct ifinfomsg);
240         if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
241                 r_name->data, r_name->len) != NM_OK)) {
242             nm_bug("%s: Error add_attr", __func__);
243         }
244 
245         peer_ifm = RTA_DATA(vdata);
246         peer_ifm->ifi_index = 0;
247         peer_ifm->ifi_flags = ifm->ifi_flags;
248         peer_ifm->ifi_change = ifm->ifi_change;
249         ifm->ifi_flags = ifi_flags;
250         ifm->ifi_change = ifi_change;
251 
252         vdata->rta_len = (char *) NLMSG_TAIL(&req.n) - (char *) vdata;
253     }
254 
255     nm_net_add_attr_nest_end(&req.n, data);
256     nm_net_add_attr_nest_end(&req.n, linkinfo);
257 
258     nm_net_rtnl_open(&rth);
259     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
260     close(rth.sd);
261 }
262 
nm_net_del_iface(const nm_str_t * name)263 void nm_net_del_iface(const nm_str_t *name)
264 {
265     struct iplink_req req;
266     struct rtnl_handle rth;
267     uint32_t dev_index;
268 
269     memset(&req, 0, sizeof(req));
270 
271     if ((dev_index = if_nametoindex(name->data)) == 0)
272         nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
273 
274     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
275     req.n.nlmsg_flags = NLM_F_REQUEST;
276     req.n.nlmsg_type = RTM_DELLINK;
277 
278     req.i.ifi_family = AF_UNSPEC;
279     req.i.ifi_index = dev_index;
280 
281     nm_net_rtnl_open(&rth);
282     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
283     close(rth.sd);
284 }
285 #endif /* NM_OS_LINUX */
286 
nm_net_set_ipaddr(const nm_str_t * name,const nm_str_t * addr)287 void nm_net_set_ipaddr(const nm_str_t *name, const nm_str_t *addr)
288 {
289     nm_net_addr_change(name, addr, NM_SET_LINK_ADDR);
290 }
291 
nm_net_set_altname(const nm_str_t * name,const nm_str_t * altname)292 void nm_net_set_altname(const nm_str_t *name, const nm_str_t *altname)
293 {
294 #if defined (NM_WITH_NEWLINKPROP)
295     uint32_t dev_index;
296     struct rtnl_handle rth;
297     struct rtattr *props;
298     struct iplink_req req = {
299         .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
300         .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND,
301         .n.nlmsg_type = RTM_NEWLINKPROP,
302         .i.ifi_family = AF_UNSPEC
303     };
304 
305     props = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_PROP_LIST | NLA_F_NESTED);
306     nm_net_add_attr(&req.n, sizeof(req), IFLA_ALT_IFNAME,
307             altname->data, altname->len + 1);
308     nm_net_add_attr_nest_end(&req.n, props);
309 
310     if ((dev_index = if_nametoindex(name->data)) == 0)
311         nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
312     req.i.ifi_index = dev_index;
313 
314     nm_net_rtnl_open(&rth);
315     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
316     close(rth.sd);
317 #else
318     (void) name;
319     (void) altname;
320 #endif /* NM_WITH_NEWLINKPROP */
321 }
322 
nm_net_verify_mac(const nm_str_t * mac)323 int nm_net_verify_mac(const nm_str_t *mac)
324 {
325     int rc = NM_ERR;
326     const char *regex = "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$";
327     regex_t reg;
328 
329     if (regcomp(&reg, regex, REG_EXTENDED) != 0) {
330         nm_bug("%s: regcomp failed", __func__);
331     }
332 
333     if (regexec(&reg, mac->data, 0, NULL, 0) == 0)
334         rc = NM_OK;
335 
336     regfree(&reg);
337 
338     return rc;
339 }
340 
nm_net_verify_ipaddr4(const nm_str_t * src,nm_net_addr_t * net,nm_str_t * err)341 int nm_net_verify_ipaddr4(const nm_str_t *src, nm_net_addr_t *net,
342                           nm_str_t *err)
343 {
344     int rc = NM_OK;
345     nm_str_t buf = NM_INIT_STR;
346     nm_str_t addr = NM_INIT_STR;
347     char *token, *saveptr;
348     nm_net_addr_t netaddr = NM_INIT_NETADDR;
349     int n = 0;
350 
351     nm_str_copy(&buf, src);
352     saveptr = buf.data;
353 
354     if (src->data[src->len - 1] == '/') {
355         nm_str_alloc_text(err, _("Invalid address format: expected IPv4/CIDR"));
356         rc = NM_ERR;
357         goto out;
358     }
359 
360     while ((token = strtok_r(saveptr, "/", &saveptr))) {
361         switch (n) {
362         case 0:
363             nm_str_alloc_text(&addr, token);
364             break;
365         case 1:
366             {
367                 nm_str_t tmp = NM_INIT_STR;
368                 nm_str_alloc_text(&tmp, token);
369                 netaddr.cidr = nm_str_stoui(&tmp, 10);
370                 nm_str_free(&tmp);
371             }
372             break;
373         default:
374             nm_str_alloc_text(err, _("Invalid address format: expected IPv4/CIDR"));
375             rc = NM_ERR;
376             goto out;
377         }
378         n++;
379     }
380 
381     if ((addr.len == 0) ||
382         (inet_pton(AF_INET, addr.data, &netaddr.addr.s_addr) != 1)) {
383         nm_str_alloc_text(err, _("Invalid IPv4 address"));
384         rc = NM_ERR;
385         goto out;
386     }
387 
388     if ((netaddr.cidr > 32) || (netaddr.cidr == 0)) {
389         nm_str_alloc_text(err, _("Invalid CIDR: expected [1-32]"));
390         rc = NM_ERR;
391         goto out;
392     }
393 
394     if (net != NULL) {
395         net->cidr = netaddr.cidr;
396         memcpy(&net->addr, &netaddr.addr, sizeof(struct in_addr));
397     }
398 
399 out:
400     nm_str_free(&addr);
401     nm_str_free(&buf);
402     return rc;
403 }
404 
nm_net_fix_tap_name(nm_str_t * name,const nm_str_t * maddr)405 int nm_net_fix_tap_name(nm_str_t *name, const nm_str_t *maddr)
406 {
407     nm_str_t maddr_copy = NM_INIT_STR;
408 
409     if (name->len <= 15) /* Linux tap iface max name len */
410         return 0;
411 
412     nm_str_copy(&maddr_copy, maddr);
413     nm_str_remove_char(&maddr_copy, ':');
414 
415     nm_str_format(name, "vm-%s", maddr_copy.data);
416 
417     nm_str_free(&maddr_copy);
418 
419     return 1;
420 }
421 
nm_net_mac_n2s(uint64_t maddr,nm_str_t * res)422 void nm_net_mac_n2s(uint64_t maddr, nm_str_t *res)
423 {
424     char buf[64] = {0};
425     int pos = 0;
426 
427     for (int byte = 0; byte < 6; byte++) {
428         uint32_t octet = ((maddr >> 40) & 0xff);
429 
430         int n = snprintf(buf + pos, sizeof(buf) - pos, "%02x:", octet);
431         if (n < 0 || n >= (int)(sizeof(buf) - pos))
432             nm_bug(_("%s: snprintf failed"), __func__);
433 
434         pos += n;
435         maddr <<= 8;
436     }
437 
438     buf[--pos] = '\0';
439 
440     nm_str_alloc_text(res, buf);
441 }
442 
nm_net_mac_s2a(const nm_str_t * addr,char * res,size_t len)443 static size_t nm_net_mac_s2a(const nm_str_t *addr, char *res, size_t len)
444 {
445     nm_str_t copy = NM_INIT_STR;
446     size_t n = 0;
447     char *savep;
448 
449     nm_str_copy(&copy, addr);
450     savep = copy.data;
451 
452     for (; n < len; n++) {
453         char *cp = strchr(copy.data, ':');
454 
455         if (cp) {
456             *cp = '\0';
457             cp++;
458         }
459 
460         res[n] = nm_str_stoui(&copy, 16);
461 
462         if (!cp)
463             break;
464         copy.data = cp;
465     }
466 
467     copy.data = savep;
468     nm_str_free(&copy);
469 
470     return n + 1;
471 }
472 
nm_net_mac_s2n(const nm_str_t * addr)473 uint64_t nm_net_mac_s2n(const nm_str_t *addr)
474 {
475     uint64_t mac = 0;
476     unsigned char buf[6];
477     const size_t buf_len = nm_arr_len(buf);
478 
479     nm_net_mac_s2a(addr, (char *)buf, buf_len);
480 
481     for (size_t i = 0; i < buf_len; ++i)
482         mac |= ((uint64_t)buf[i]) << 8 * (buf_len - 1 - i);
483 
484     return mac;
485 }
486 
nm_net_manage_tap(const nm_str_t * name,int on_off)487 static void nm_net_manage_tap(const nm_str_t *name, int on_off)
488 {
489     struct ifreq ifr;
490 
491     memset(&ifr, 0, sizeof(ifr));
492 #if defined (NM_OS_LINUX)
493     int fd;
494     ifr.ifr_flags |= (IFF_NO_PI | IFF_TAP);
495     nm_strlcpy(ifr.ifr_name, name->data, IFNAMSIZ);
496 
497     if ((fd = open(NM_TUNDEV, O_RDWR)) < 0)
498         nm_bug(_("%s: cannot open TUN device: %s"), __func__, strerror(errno));
499 
500     if (ioctl(fd, TUNSETIFF, &ifr) == -1)
501         nm_bug("%s: ioctl(TUNSETIFF): %s", __func__, strerror(errno));
502 
503     if (ioctl(fd, TUNSETPERSIST, on_off) == -1)
504         nm_bug("%s: ioctl(TUNSETPERSIST): %s", __func__, strerror(errno));
505 
506     if (on_off == NM_TAP_ON)
507         nm_net_link_up(name);
508 
509     close(fd);
510 #elif defined (NM_OS_FREEBSD)
511     int sock = socket(AF_INET, SOCK_DGRAM, 0);
512     if (sock == -1)
513         nm_bug("%s: socket: %s", __func__, strerror(errno));
514 
515     strlcpy(ifr.ifr_name, name->data, sizeof(ifr.ifr_name));
516 
517     if (on_off == NM_TAP_OFF) {
518         if (ioctl(sock, SIOCIFDESTROY, &ifr) == -1)
519             nm_bug("%s: ioctl(SIOCIFDESTROY): %s", __func__, strerror(errno));
520     }
521     close(sock);
522 #endif /* NM_OS_LINUX */
523 }
524 
525 #if defined (NM_OS_LINUX)
nm_net_rtnl_open(struct rtnl_handle * rth)526 static void nm_net_rtnl_open(struct rtnl_handle *rth)
527 {
528     memset(rth, 0, sizeof(*rth));
529 
530     rth->sa.nl_family = AF_NETLINK;
531     rth->sa.nl_groups = 0;
532 
533     if ((rth->sd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE)) == -1)
534         nm_bug("%s: cannot open netlink socket: %s", __func__, strerror(errno));
535 
536     if (bind(rth->sd, (struct sockaddr *) &rth->sa, sizeof(rth->sa)) == -1) {
537         close(rth->sd);
538         nm_bug("%s: cannot bind netlink socket: %s", __func__, strerror(errno));
539     }
540 
541     rth->seq = time(NULL);
542 }
543 
nm_net_add_attr(struct nlmsghdr * n,size_t mlen,int type,const void * data,size_t dlen)544 static int nm_net_add_attr(struct nlmsghdr *n, size_t mlen,
545                            int type, const void *data, size_t dlen)
546 {
547     size_t len = RTA_LENGTH(dlen);
548     struct rtattr *rta;
549 
550     if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > mlen) {
551         return NM_ERR;
552     }
553 
554     rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN(n->nlmsg_len));
555     rta->rta_type = type;
556     rta->rta_len = len;
557     if (data)
558         memcpy(RTA_DATA(rta), data, dlen);
559     n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
560 
561     return NM_OK;
562 }
563 
nm_net_add_attr_nest(struct nlmsghdr * n,size_t mlen,int type)564 static struct rtattr *nm_net_add_attr_nest(struct nlmsghdr *n, size_t mlen,
565                                            int type)
566 {
567     struct rtattr *nest = NLMSG_TAIL(n);
568 
569     if (nm_net_add_attr(n, mlen, type, NULL, 0) != NM_OK)
570         nm_bug("%s: Error add_attr", __func__);
571 
572     return nest;
573 }
574 
nm_net_add_attr_nest_end(struct nlmsghdr * n,struct rtattr * nest)575 static int nm_net_add_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
576 {
577     nest->rta_len = (char *) NLMSG_TAIL(n) - (char *) nest;
578 
579     return n->nlmsg_len;
580 }
581 
nm_net_rtnl_talk(struct rtnl_handle * rth,struct nlmsghdr * n,struct nlmsghdr * res,size_t res_len)582 static void nm_net_rtnl_talk(struct rtnl_handle *rth, struct nlmsghdr *n,
583                              struct nlmsghdr *res, size_t res_len)
584 {
585     ssize_t len;
586     struct sockaddr_nl sa;
587     struct iovec iov;
588     struct msghdr msg;
589     struct nlmsghdr *nh;
590     char buf[16384] = {0};
591 
592     iov.iov_base = (void *) n;
593     iov.iov_len = n->nlmsg_len;
594 
595     memset(&msg, 0, sizeof(msg));
596     msg.msg_name = &sa;
597     msg.msg_namelen = sizeof(sa);
598     msg.msg_iov = &iov;
599     msg.msg_iovlen = 1;
600 
601     memset(&sa, 0, sizeof(sa));
602     sa.nl_family = AF_NETLINK;
603 
604     n->nlmsg_flags |= NLM_F_ACK;
605     n->nlmsg_seq = ++rth->seq;
606 
607     if ((len = sendmsg(rth->sd, &msg, 0)) < 0)
608         nm_bug("%s: cannot talk to rtnetlink", __func__);
609 
610     memset(buf, 0, sizeof(buf));
611 
612     iov.iov_base = buf;
613     iov.iov_len = sizeof(buf);
614 
615     len = recvmsg(rth->sd, &msg, 0);
616 
617     for (nh = (struct nlmsghdr *) buf; NLMSG_OK(nh, len);
618          nh = NLMSG_NEXT(nh, len)) {
619         if (nh->nlmsg_type == NLMSG_DONE)
620             return;
621         if (nh->nlmsg_type == NLMSG_ERROR) {
622             struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh);
623             if (!nlerr->error)
624                 return;
625             nm_bug("%s: RTNETLINK answers: %s",
626                 __func__, strerror(-nlerr->error));
627         }
628         if (res)
629             memcpy(res, nh, nm_min(res_len, nh->nlmsg_len));
630     }
631 }
632 
nm_net_link_up(const nm_str_t * name)633 void nm_net_link_up(const nm_str_t *name)
634 {
635     nm_net_set_link_status(name, NM_SET_LINK_UP);
636 }
637 
nm_net_link_down(const nm_str_t * name)638 void nm_net_link_down(const nm_str_t *name)
639 {
640     nm_net_set_link_status(name, NM_SET_LINK_DOWN);
641 }
642 
nm_net_link_status(const nm_str_t * name)643 int nm_net_link_status(const nm_str_t *name)
644 {
645     struct iplink_req req;
646     struct rtnl_handle rth;
647     struct {
648         struct nlmsghdr n;
649         /* cppcheck-suppress unusedStructMember */
650         char buf[16384];
651     } result;
652 
653     memset(&req, 0, sizeof(req));
654 
655     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
656     req.n.nlmsg_flags = NLM_F_REQUEST;
657     req.n.nlmsg_type = RTM_GETLINK;
658     req.i.ifi_family = AF_UNSPEC;
659 
660     if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
661             name->data, name->len) != NM_OK)) {
662         nm_bug("%s: Error add_attr", __func__);
663     }
664 
665     nm_net_rtnl_open(&rth);
666     nm_net_rtnl_talk(&rth, &req.n, &result.n, sizeof(result));
667     close(rth.sd);
668 
669     {
670         struct ifinfomsg *ifi = NLMSG_DATA(&result.n);
671         if (!(ifi->ifi_flags & IFF_UP))
672             return NM_ERR;
673     }
674 
675     return NM_OK;
676 }
677 
nm_net_set_link_status(const nm_str_t * name,int action)678 static void nm_net_set_link_status(const nm_str_t *name, int action)
679 {
680     struct iplink_req req;
681     struct rtnl_handle rth;
682     uint32_t dev_index;
683 
684     memset(&req, 0, sizeof(req));
685 
686     if ((dev_index = if_nametoindex(name->data)) == 0)
687         nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
688 
689     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
690     req.n.nlmsg_flags = NLM_F_REQUEST;
691     req.n.nlmsg_type = RTM_NEWLINK;
692 
693     req.i.ifi_family = AF_UNSPEC;
694     req.i.ifi_index = dev_index;
695     req.i.ifi_change |= IFF_UP;
696 
697     switch (action) {
698     case NM_SET_LINK_UP:
699         req.i.ifi_flags |= IFF_UP;
700         break;
701     case NM_SET_LINK_DOWN:
702         req.i.ifi_flags &= ~IFF_UP;
703         break;
704     }
705 
706     nm_net_rtnl_open(&rth);
707     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
708     close(rth.sd);
709 }
710 #endif /* NM_OS_LINUX */
711 
nm_net_addr_change(const nm_str_t * name,const nm_str_t * src,int action)712 static void nm_net_addr_change(const nm_str_t *name, const nm_str_t *src,
713                                int action)
714 {
715 #if defined (NM_OS_LINUX)
716     struct ipaddr_req req;
717     struct rtnl_handle rth;
718     uint32_t dev_index;
719 
720     memset(&req, 0, sizeof(req));
721 
722     if ((dev_index = if_nametoindex(name->data)) == 0)
723         nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
724 
725     req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
726     req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
727     req.n.nlmsg_type = RTM_NEWADDR;
728 
729     req.i.ifa_family = AF_INET;
730     req.i.ifa_index = dev_index;
731     req.i.ifa_scope = 0;
732 
733     switch (action) {
734     case NM_SET_LINK_ADDR:
735         {
736             struct in_addr brd;
737             struct in_addr mask;
738             nm_net_addr_t net = NM_INIT_NETADDR;
739             nm_str_t errmsg = NM_INIT_STR;
740 
741             if (nm_net_verify_ipaddr4(src, &net, &errmsg) != NM_OK)
742                 nm_bug("%s: %s", __func__, errmsg.data);
743 
744             mask.s_addr = ~((1 << (32 - net.cidr)) - 1);
745             mask.s_addr = htonl(mask.s_addr);
746             brd.s_addr = net.addr.s_addr | (~mask.s_addr);
747             req.i.ifa_prefixlen = net.cidr;
748 
749             if ((nm_net_add_attr(&req.n, sizeof(req), IFA_LOCAL,
750                     &net.addr.s_addr, sizeof(net.addr.s_addr)) != NM_OK) ||
751                 (nm_net_add_attr(&req.n, sizeof(req), IFA_BROADCAST,
752                     &brd.s_addr, sizeof(brd.s_addr)) != NM_OK)) {
753                 nm_bug("%s: Error add_attr", __func__);
754             }
755 
756             nm_str_free(&errmsg);
757         }
758         break;
759     }
760 
761     nm_net_rtnl_open(&rth);
762     nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
763     close(rth.sd);
764 #else
765     (void) name;
766     (void) action;
767     (void) src;
768 #endif /* NM_OS_LINUX */
769 }
770 
nm_net_check_port(const uint16_t port,const int type,const uint32_t inaddr)771 int nm_net_check_port(const uint16_t port, const int type, const uint32_t inaddr)
772 {
773     int ret = 0;
774     struct sockaddr_in addr;
775 
776     int sock = socket(AF_INET, type, 0);
777     if (!sock)
778         nm_bug("%s: couldn't create socket", __func__);
779 
780     addr.sin_family = AF_INET;
781     addr.sin_port = htons(port);
782     addr.sin_addr.s_addr = htonl(inaddr);
783 
784     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
785         ret = 1;
786         shutdown(sock, SHUT_RDWR);
787     }
788 
789     close(sock);
790 
791     return ret;
792 }
793 
794 /* vim:set ts=4 sw=4: */
795