1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <net/if.h>
36
37 #include <ctype.h>
38 #include <err.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <ifaddrs.h>
44
45 #include <netinet/in.h>
46 #include <netinet/in_var.h>
47 #include <arpa/inet.h>
48 #include <netdb.h>
49
50 #include "ifconfig.h"
51 #include "ifconfig_netlink.h"
52
53 #ifdef WITHOUT_NETLINK
54 static struct in_aliasreq in_addreq;
55 static struct ifreq in_ridreq;
56 #else
57 struct in_px {
58 struct in_addr addr;
59 int plen;
60 bool addrset;
61 bool maskset;
62 };
63 struct in_pdata {
64 struct in_px addr;
65 struct in_px dst_addr;
66 struct in_px brd_addr;
67 uint32_t flags;
68 uint32_t vhid;
69 };
70 static struct in_pdata in_add, in_del;
71 #endif
72
73 static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/
74 extern char *f_inet, *f_addr;
75
76 static void
print_addr(struct sockaddr_in * sin)77 print_addr(struct sockaddr_in *sin)
78 {
79 int error, n_flags;
80
81 if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0)
82 n_flags = 0;
83 else if (f_addr != NULL && strcmp(f_addr, "host") == 0)
84 n_flags = NI_NOFQDN;
85 else
86 n_flags = NI_NUMERICHOST;
87
88 error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf,
89 sizeof(addr_buf), NULL, 0, n_flags);
90
91 if (error)
92 inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf));
93
94 printf("\tinet %s", addr_buf);
95 }
96
97 #ifdef WITHOUT_NETLINK
98 static void
in_status(if_ctx * ctx __unused,const struct ifaddrs * ifa)99 in_status(if_ctx *ctx __unused, const struct ifaddrs *ifa)
100 {
101 struct sockaddr_in *sin, null_sin = {};
102
103 sin = satosin(ifa->ifa_addr);
104 if (sin == NULL)
105 return;
106
107 print_addr(sin);
108
109 if (ifa->ifa_flags & IFF_POINTOPOINT) {
110 sin = satosin(ifa->ifa_dstaddr);
111 if (sin == NULL)
112 sin = &null_sin;
113 printf(" --> %s", inet_ntoa(sin->sin_addr));
114 }
115
116 sin = satosin(ifa->ifa_netmask);
117 if (sin == NULL)
118 sin = &null_sin;
119 if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
120 int cidr = 32;
121 unsigned long smask;
122
123 smask = ntohl(sin->sin_addr.s_addr);
124 while ((smask & 1) == 0) {
125 smask = smask >> 1;
126 cidr--;
127 if (cidr == 0)
128 break;
129 }
130 printf("/%d", cidr);
131 } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0)
132 printf(" netmask %s", inet_ntoa(sin->sin_addr));
133 else
134 printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr));
135
136 if (ifa->ifa_flags & IFF_BROADCAST) {
137 sin = satosin(ifa->ifa_broadaddr);
138 if (sin != NULL && sin->sin_addr.s_addr != 0)
139 printf(" broadcast %s", inet_ntoa(sin->sin_addr));
140 }
141
142 print_vhid(ifa);
143
144 putchar('\n');
145 }
146
147 #else
148 static struct in_addr
get_mask(int plen)149 get_mask(int plen)
150 {
151 struct in_addr a;
152
153 a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
154
155 return (a);
156 }
157
158 static void
in_status_nl(if_ctx * ctx __unused,if_link_t * link,if_addr_t * ifa)159 in_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa)
160 {
161 struct sockaddr_in *sin = satosin(ifa->ifa_local);
162 int plen = ifa->ifa_prefixlen;
163
164 print_addr(sin);
165
166 if (link->ifi_flags & IFF_POINTOPOINT) {
167 struct sockaddr_in *dst = satosin(ifa->ifa_address);
168
169 printf(" --> %s", inet_ntoa(dst->sin_addr));
170 }
171 if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
172 printf("/%d", plen);
173 } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0)
174 printf(" netmask %s", inet_ntoa(get_mask(plen)));
175 else
176 printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr));
177
178 if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) {
179 struct sockaddr_in *brd = satosin(ifa->ifa_broadcast);
180 if (brd != NULL)
181 printf(" broadcast %s", inet_ntoa(brd->sin_addr));
182 }
183
184 if (ifa->ifaf_vhid != 0)
185 printf(" vhid %d", ifa->ifaf_vhid);
186
187 putchar('\n');
188 }
189 #endif
190
191
192 #ifdef WITHOUT_NETLINK
193 #define SIN(x) ((struct sockaddr_in *) &(x))
194 static struct sockaddr_in *sintab[] = {
195 SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
196 SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)
197 };
198
199 static void
in_copyaddr(if_ctx * ctx __unused,int to,int from)200 in_copyaddr(if_ctx *ctx __unused, int to, int from)
201 {
202 memcpy(sintab[to], sintab[from], sizeof(struct sockaddr_in));
203 }
204
205 static void
in_getaddr(const char * s,int which)206 in_getaddr(const char *s, int which)
207 {
208 struct sockaddr_in *sin = sintab[which];
209 struct hostent *hp;
210 struct netent *np;
211
212 sin->sin_len = sizeof(*sin);
213 sin->sin_family = AF_INET;
214
215 if (which == ADDR) {
216 char *p = NULL;
217
218 if((p = strrchr(s, '/')) != NULL) {
219 const char *errstr;
220 /* address is `name/masklen' */
221 int masklen = 0;
222 struct sockaddr_in *min = sintab[MASK];
223 *p = '\0';
224 if (!isdigit(*(p + 1)))
225 errstr = "invalid";
226 else
227 masklen = (int)strtonum(p + 1, 0, 32, &errstr);
228 if (errstr != NULL) {
229 *p = '/';
230 errx(1, "%s: bad value (width %s)", s, errstr);
231 }
232 min->sin_family = AF_INET;
233 min->sin_len = sizeof(*min);
234 min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) &
235 0xffffffff);
236 }
237 }
238
239 if (inet_aton(s, &sin->sin_addr))
240 return;
241 if ((hp = gethostbyname(s)) != NULL)
242 bcopy(hp->h_addr, (char *)&sin->sin_addr,
243 MIN((size_t)hp->h_length, sizeof(sin->sin_addr)));
244 else if ((np = getnetbyname(s)) != NULL)
245 sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
246 else
247 errx(1, "%s: bad value", s);
248 }
249
250 #else
251
252 static struct in_px *sintab_nl[] = {
253 &in_del.addr, /* RIDADDR */
254 &in_add.addr, /* ADDR */
255 NULL, /* MASK */
256 &in_add.dst_addr, /* DSTADDR*/
257 &in_add.brd_addr, /* BRDADDR*/
258 };
259
260 static void
in_copyaddr(if_ctx * ctx __unused,int to,int from)261 in_copyaddr(if_ctx *ctx __unused, int to, int from)
262 {
263 sintab_nl[to]->addr = sintab_nl[from]->addr;
264 sintab_nl[to]->addrset = sintab_nl[from]->addrset;
265 }
266
267 static void
in_getip(const char * addr_str,struct in_addr * ip)268 in_getip(const char *addr_str, struct in_addr *ip)
269 {
270 struct hostent *hp;
271 struct netent *np;
272
273 if (inet_aton(addr_str, ip))
274 return;
275 if ((hp = gethostbyname(addr_str)) != NULL)
276 bcopy(hp->h_addr, (char *)ip,
277 MIN((size_t)hp->h_length, sizeof(ip)));
278 else if ((np = getnetbyname(addr_str)) != NULL)
279 *ip = inet_makeaddr(np->n_net, INADDR_ANY);
280 else
281 errx(1, "%s: bad value", addr_str);
282 }
283
284 static void
in_getaddr(const char * s,int which)285 in_getaddr(const char *s, int which)
286 {
287 struct in_px *px = sintab_nl[which];
288
289 if (which == MASK) {
290 struct in_px *px_addr = sintab_nl[ADDR];
291 struct in_addr mask = {};
292
293 in_getip(s, &mask);
294 px_addr->plen = __bitcount32(mask.s_addr);
295 px_addr->maskset = true;
296 return;
297 }
298
299 if (which == ADDR) {
300 char *p = NULL;
301
302 if((p = strrchr(s, '/')) != NULL) {
303 const char *errstr;
304 /* address is `name/masklen' */
305 int masklen;
306 *p = '\0';
307 if (!isdigit(*(p + 1)))
308 errstr = "invalid";
309 else
310 masklen = (int)strtonum(p + 1, 0, 32, &errstr);
311 if (errstr != NULL) {
312 *p = '/';
313 errx(1, "%s: bad value (width %s)", s, errstr);
314 }
315 px->plen = masklen;
316 px->maskset = true;
317 }
318 }
319
320 in_getip(s, &px->addr);
321 px->addrset = true;
322 }
323
324 /*
325 * Deletes the first found IPv4 interface address for the interface.
326 *
327 * This function provides SIOCDIFADDR semantics missing in Netlink.
328 * When no valid IPv4 address is specified (sin_family or sin_len is wrong) to
329 * the SIOCDIFADDR call, it deletes the first found IPv4 address on the interface.
330 * 'ifconfig IFNAME inet addr/prefix' relies on that behavior, as it
331 * executes empty SIOCDIFADDR before adding a new address.
332 */
333 static int
in_delete_first_nl(if_ctx * ctx)334 in_delete_first_nl(if_ctx *ctx)
335 {
336 struct nlmsghdr *hdr;
337 struct ifaddrmsg *ifahdr;
338 uint32_t nlmsg_seq;
339 struct in_addr addr;
340 struct snl_writer nw = {};
341 struct snl_errmsg_data e = {};
342 struct snl_state *ss = ctx->io_ss;
343 bool found = false;
344
345 uint32_t ifindex = if_nametoindex_nl(ss, ctx->ifname);
346 if (ifindex == 0) {
347 /* No interface with the desired name, nothing to delete */
348 return (EADDRNOTAVAIL);
349 }
350
351 snl_init_writer(ss, &nw);
352 hdr = snl_create_msg_request(&nw, NL_RTM_GETADDR);
353 hdr->nlmsg_flags |= NLM_F_DUMP;
354 ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
355 ifahdr->ifa_family = AF_INET;
356 ifahdr->ifa_index = ifindex;
357
358 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
359 return (EINVAL);
360
361 nlmsg_seq = hdr->nlmsg_seq;
362 while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
363 struct snl_parsed_addr attrs = {};
364 if (snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) {
365 addr = satosin(attrs.ifa_local)->sin_addr;
366 ifindex = attrs.ifa_index;
367 found = true;
368 break;
369 } else
370 return (EINVAL);
371 }
372 if (e.error != 0) {
373 if (e.error_str != NULL)
374 warnx("%s(): %s", __func__, e.error_str);
375 return (e.error);
376 }
377
378 if (!found)
379 return (0);
380
381 /* Try to delete the found address */
382 snl_init_writer(ss, &nw);
383 hdr = snl_create_msg_request(&nw, NL_RTM_DELADDR);
384 ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
385 ifahdr->ifa_family = AF_INET;
386 ifahdr->ifa_index = ifindex;
387 snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr);
388
389 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
390 return (EINVAL);
391 memset(&e, 0, sizeof(e));
392 snl_read_reply_code(ss, hdr->nlmsg_seq, &e);
393 if (e.error_str != NULL)
394 warnx("%s(): %s", __func__, e.error_str);
395
396 return (e.error);
397 }
398
399
400 static int
in_exec_nl(if_ctx * ctx,unsigned long action,void * data)401 in_exec_nl(if_ctx *ctx, unsigned long action, void *data)
402 {
403 struct in_pdata *pdata = (struct in_pdata *)data;
404 struct snl_writer nw = {};
405
406 if (action == NL_RTM_DELADDR && !pdata->addr.addrset)
407 return (in_delete_first_nl(ctx));
408
409 snl_init_writer(ctx->io_ss, &nw);
410 struct nlmsghdr *hdr = snl_create_msg_request(&nw, action);
411 struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
412
413 ifahdr->ifa_family = AF_INET;
414 ifahdr->ifa_prefixlen = pdata->addr.plen;
415 ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname);
416
417 snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr);
418 if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset)
419 snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr);
420 if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset)
421 snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr);
422
423 int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD);
424 snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags);
425 if (pdata->vhid != 0)
426 snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid);
427 snl_end_attr_nested(&nw, off);
428
429 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr))
430 return (0);
431
432 struct snl_errmsg_data e = {};
433 snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e);
434 if (e.error_str != NULL)
435 warnx("%s(): %s", __func__, e.error_str);
436
437 return (e.error);
438 }
439 #endif
440
441 static void
err_nomask(int ifflags)442 err_nomask(int ifflags)
443 {
444 if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
445 errx(1, "ERROR: setting interface address without mask is no longer supported.");
446 }
447 }
448
449 static void
in_postproc(if_ctx * ctx __unused,int newaddr,int ifflags)450 in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags)
451 {
452 #ifdef WITHOUT_NETLINK
453 if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) {
454 err_nomask(ifflags);
455 }
456 #else
457 if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) {
458 err_nomask(ifflags);
459 }
460 #endif
461 }
462
463 static void
in_status_tunnel(if_ctx * ctx)464 in_status_tunnel(if_ctx *ctx)
465 {
466 char src[NI_MAXHOST];
467 char dst[NI_MAXHOST];
468 struct ifreq ifr;
469 const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr;
470
471 memset(&ifr, 0, sizeof(ifr));
472 strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ);
473
474 if (ioctl_ctx(ctx, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0)
475 return;
476 if (sa->sa_family != AF_INET)
477 return;
478 if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0)
479 src[0] = '\0';
480
481 if (ioctl_ctx(ctx, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0)
482 return;
483 if (sa->sa_family != AF_INET)
484 return;
485 if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0)
486 dst[0] = '\0';
487
488 printf("\ttunnel inet %s --> %s\n", src, dst);
489 }
490
491 static void
in_set_tunnel(if_ctx * ctx,struct addrinfo * srcres,struct addrinfo * dstres)492 in_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres)
493 {
494 struct in_aliasreq addreq;
495
496 memset(&addreq, 0, sizeof(addreq));
497 strlcpy(addreq.ifra_name, ctx->ifname, IFNAMSIZ);
498 memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
499 memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len);
500
501 if (ioctl_ctx(ctx, SIOCSIFPHYADDR, &addreq) < 0)
502 warn("SIOCSIFPHYADDR");
503 }
504
505 static void
in_set_vhid(int vhid)506 in_set_vhid(int vhid)
507 {
508 #ifdef WITHOUT_NETLINK
509 in_addreq.ifra_vhid = vhid;
510 #else
511 in_add.vhid = (uint32_t)vhid;
512 #endif
513 }
514
515 static struct afswtch af_inet = {
516 .af_name = "inet",
517 .af_af = AF_INET,
518 #ifdef WITHOUT_NETLINK
519 .af_status = in_status,
520 #else
521 .af_status = in_status_nl,
522 #endif
523 .af_getaddr = in_getaddr,
524 .af_copyaddr = in_copyaddr,
525 .af_postproc = in_postproc,
526 .af_status_tunnel = in_status_tunnel,
527 .af_settunnel = in_set_tunnel,
528 .af_setvhid = in_set_vhid,
529 #ifdef WITHOUT_NETLINK
530 .af_difaddr = SIOCDIFADDR,
531 .af_aifaddr = SIOCAIFADDR,
532 .af_ridreq = &in_ridreq,
533 .af_addreq = &in_addreq,
534 .af_exec = af_exec_ioctl,
535 #else
536 .af_difaddr = NL_RTM_DELADDR,
537 .af_aifaddr = NL_RTM_NEWADDR,
538 .af_ridreq = &in_del,
539 .af_addreq = &in_add,
540 .af_exec = in_exec_nl,
541 #endif
542 };
543
544 static __constructor void
inet_ctor(void)545 inet_ctor(void)
546 {
547
548 #ifndef RESCUE
549 if (!feature_present("inet"))
550 return;
551 #endif
552 af_register(&af_inet);
553 }
554