xref: /freebsd/sbin/ifconfig/af_inet.c (revision 6bfca4dcab07dad45a805879d954876b353c0810)
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
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
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
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
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
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
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
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
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
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
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
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 
440 static void
441 in_setdefaultmask_nl(void)
442 {
443         struct in_px *px = sintab_nl[ADDR];
444 
445 	in_addr_t i = ntohl(px->addr.s_addr);
446 
447 	/*
448 	 * If netmask isn't supplied, use historical default.
449 	 * This is deprecated for interfaces other than loopback
450 	 * or point-to-point; warn in other cases.  In the future
451 	 * we should return an error rather than warning.
452 	 */
453 	if (IN_CLASSA(i))
454 		px->plen = IN_CLASSA_NSHIFT;
455 	else if (IN_CLASSB(i))
456 		px->plen = IN_CLASSB_NSHIFT;
457 	else
458 		px->plen = IN_CLASSC_NSHIFT;
459 	px->maskset = true;
460 }
461 #endif
462 
463 static void
464 warn_nomask(int ifflags)
465 {
466     if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
467 	warnx("WARNING: setting interface address without mask "
468 	    "is deprecated,\ndefault mask may not be correct.");
469     }
470 }
471 
472 static void
473 in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags)
474 {
475 #ifdef WITHOUT_NETLINK
476 	if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) {
477 		warn_nomask(ifflags);
478 	}
479 #else
480 	if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) {
481 		warn_nomask(ifflags);
482 	    in_setdefaultmask_nl();
483 	}
484 #endif
485 }
486 
487 static void
488 in_status_tunnel(if_ctx *ctx)
489 {
490 	char src[NI_MAXHOST];
491 	char dst[NI_MAXHOST];
492 	struct ifreq ifr;
493 	const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr;
494 
495 	memset(&ifr, 0, sizeof(ifr));
496 	strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ);
497 
498 	if (ioctl_ctx(ctx, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0)
499 		return;
500 	if (sa->sa_family != AF_INET)
501 		return;
502 	if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0)
503 		src[0] = '\0';
504 
505 	if (ioctl_ctx(ctx, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0)
506 		return;
507 	if (sa->sa_family != AF_INET)
508 		return;
509 	if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0)
510 		dst[0] = '\0';
511 
512 	printf("\ttunnel inet %s --> %s\n", src, dst);
513 }
514 
515 static void
516 in_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres)
517 {
518 	struct in_aliasreq addreq;
519 
520 	memset(&addreq, 0, sizeof(addreq));
521 	strlcpy(addreq.ifra_name, ctx->ifname, IFNAMSIZ);
522 	memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
523 	memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len);
524 
525 	if (ioctl_ctx(ctx, SIOCSIFPHYADDR, &addreq) < 0)
526 		warn("SIOCSIFPHYADDR");
527 }
528 
529 static void
530 in_set_vhid(int vhid)
531 {
532 #ifdef WITHOUT_NETLINK
533 	in_addreq.ifra_vhid = vhid;
534 #else
535 	in_add.vhid = (uint32_t)vhid;
536 #endif
537 }
538 
539 static struct afswtch af_inet = {
540 	.af_name	= "inet",
541 	.af_af		= AF_INET,
542 #ifdef WITHOUT_NETLINK
543 	.af_status	= in_status,
544 #else
545 	.af_status	= in_status_nl,
546 #endif
547 	.af_getaddr	= in_getaddr,
548 	.af_copyaddr	= in_copyaddr,
549 	.af_postproc	= in_postproc,
550 	.af_status_tunnel = in_status_tunnel,
551 	.af_settunnel	= in_set_tunnel,
552 	.af_setvhid	= in_set_vhid,
553 #ifdef WITHOUT_NETLINK
554 	.af_difaddr	= SIOCDIFADDR,
555 	.af_aifaddr	= SIOCAIFADDR,
556 	.af_ridreq	= &in_ridreq,
557 	.af_addreq	= &in_addreq,
558 	.af_exec	= af_exec_ioctl,
559 #else
560 	.af_difaddr	= NL_RTM_DELADDR,
561 	.af_aifaddr	= NL_RTM_NEWADDR,
562 	.af_ridreq	= &in_del,
563 	.af_addreq	= &in_add,
564 	.af_exec	= in_exec_nl,
565 #endif
566 };
567 
568 static __constructor void
569 inet_ctor(void)
570 {
571 
572 #ifndef RESCUE
573 	if (!feature_present("inet"))
574 		return;
575 #endif
576 	af_register(&af_inet);
577 }
578