xref: /freebsd/sbin/ifconfig/af_inet.c (revision 5b8ce85e1a72d2c9541cb344b4e157f38f6a5d6c)
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 #ifndef lint
33 static const char rcsid[] =
34   "$FreeBSD$";
35 #endif /* not lint */
36 
37 #include <sys/param.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <net/if.h>
41 
42 #include <ctype.h>
43 #include <err.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <ifaddrs.h>
49 
50 #include <netinet/in.h>
51 #include <netinet/in_var.h>
52 #include <arpa/inet.h>
53 #include <netdb.h>
54 
55 #include "ifconfig.h"
56 #include "ifconfig_netlink.h"
57 
58 #ifdef WITHOUT_NETLINK
59 static struct in_aliasreq in_addreq;
60 static struct ifreq in_ridreq;
61 #else
62 struct in_px {
63 	struct in_addr		addr;
64 	int			plen;
65 	bool			addrset;
66 	bool			maskset;
67 };
68 struct in_pdata {
69 	struct in_px		addr;
70 	struct in_px		dst_addr;
71 	struct in_px		brd_addr;
72 	uint32_t		flags;
73 	uint32_t		vhid;
74 };
75 static struct in_pdata in_add, in_del;
76 #endif
77 
78 static char addr_buf[NI_MAXHOST];	/*for getnameinfo()*/
79 extern char *f_inet, *f_addr;
80 
81 static void
82 print_addr(struct sockaddr_in *sin)
83 {
84 	int error, n_flags;
85 
86 	if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0)
87 		n_flags = 0;
88 	else if (f_addr != NULL && strcmp(f_addr, "host") == 0)
89 		n_flags = NI_NOFQDN;
90 	else
91 		n_flags = NI_NUMERICHOST;
92 
93 	error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf,
94 			    sizeof(addr_buf), NULL, 0, n_flags);
95 
96 	if (error)
97 		inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf));
98 
99 	printf("\tinet %s", addr_buf);
100 }
101 
102 #ifdef WITHOUT_NETLINK
103 static void
104 in_status(int s __unused, const struct ifaddrs *ifa)
105 {
106 	struct sockaddr_in *sin, null_sin = {};
107 
108 	sin = (struct sockaddr_in *)ifa->ifa_addr;
109 	if (sin == NULL)
110 		return;
111 
112 	print_addr(sin);
113 
114 	if (ifa->ifa_flags & IFF_POINTOPOINT) {
115 		sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
116 		if (sin == NULL)
117 			sin = &null_sin;
118 		printf(" --> %s", inet_ntoa(sin->sin_addr));
119 	}
120 
121 	sin = (struct sockaddr_in *)ifa->ifa_netmask;
122 	if (sin == NULL)
123 		sin = &null_sin;
124 	if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
125 		int cidr = 32;
126 		unsigned long smask;
127 
128 		smask = ntohl(sin->sin_addr.s_addr);
129 		while ((smask & 1) == 0) {
130 			smask = smask >> 1;
131 			cidr--;
132 			if (cidr == 0)
133 				break;
134 		}
135 		printf("/%d", cidr);
136 	} else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0)
137 		printf(" netmask %s", inet_ntoa(sin->sin_addr));
138 	else
139 		printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr));
140 
141 	if (ifa->ifa_flags & IFF_BROADCAST) {
142 		sin = (struct sockaddr_in *)ifa->ifa_broadaddr;
143 		if (sin != NULL && sin->sin_addr.s_addr != 0)
144 			printf(" broadcast %s", inet_ntoa(sin->sin_addr));
145 	}
146 
147 	print_vhid(ifa, " ");
148 
149 	putchar('\n');
150 }
151 
152 #else
153 static struct in_addr
154 get_mask(int plen)
155 {
156 	struct in_addr a;
157 
158 	a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
159 
160 	return (a);
161 }
162 
163 static struct sockaddr_in *
164 satosin(struct sockaddr *sa)
165 {
166 	return ((struct sockaddr_in *)(void *)sa);
167 }
168 
169 static void
170 in_status_nl(struct ifconfig_args *args __unused, struct io_handler *h,
171     if_link_t *link, if_addr_t *ifa)
172 {
173 	struct sockaddr_in *sin = satosin(ifa->ifa_local);
174 	int plen = ifa->ifa_prefixlen;
175 
176 	print_addr(sin);
177 
178 	if (link->ifi_flags & IFF_POINTOPOINT) {
179 		struct sockaddr_in *dst = satosin(ifa->ifa_address);
180 
181 		printf(" --> %s", inet_ntoa(dst->sin_addr));
182 	}
183 	if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
184 		printf("/%d", plen);
185 	} else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0)
186 		printf(" netmask %s", inet_ntoa(get_mask(plen)));
187 	else
188 		printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr));
189 
190 	if ((link->ifi_flags & IFF_BROADCAST) && plen != 0)  {
191 		struct sockaddr_in *brd = satosin(ifa->ifa_broadcast);
192 		if (brd != NULL)
193 			printf(" broadcast %s", inet_ntoa(brd->sin_addr));
194 	}
195 
196 	if (ifa->ifaf_vhid != 0)
197 		printf(" vhid %d", ifa->ifaf_vhid);
198 
199 	putchar('\n');
200 }
201 #endif
202 
203 
204 #ifdef WITHOUT_NETLINK
205 #define SIN(x) ((struct sockaddr_in *) &(x))
206 static struct sockaddr_in *sintab[] = {
207 	SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
208 	SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)
209 };
210 
211 static void
212 in_getaddr(const char *s, int which)
213 {
214 	struct sockaddr_in *sin = sintab[which];
215 	struct hostent *hp;
216 	struct netent *np;
217 
218 	sin->sin_len = sizeof(*sin);
219 	sin->sin_family = AF_INET;
220 
221 	if (which == ADDR) {
222 		char *p = NULL;
223 
224 		if((p = strrchr(s, '/')) != NULL) {
225 			const char *errstr;
226 			/* address is `name/masklen' */
227 			int masklen;
228 			struct sockaddr_in *min = sintab[MASK];
229 			*p = '\0';
230 			if (!isdigit(*(p + 1)))
231 				errstr = "invalid";
232 			else
233 				masklen = (int)strtonum(p + 1, 0, 32, &errstr);
234 			if (errstr != NULL) {
235 				*p = '/';
236 				errx(1, "%s: bad value (width %s)", s, errstr);
237 			}
238 			min->sin_family = AF_INET;
239 			min->sin_len = sizeof(*min);
240 			min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) &
241 				              0xffffffff);
242 		}
243 	}
244 
245 	if (inet_aton(s, &sin->sin_addr))
246 		return;
247 	if ((hp = gethostbyname(s)) != NULL)
248 		bcopy(hp->h_addr, (char *)&sin->sin_addr,
249 		    MIN((size_t)hp->h_length, sizeof(sin->sin_addr)));
250 	else if ((np = getnetbyname(s)) != NULL)
251 		sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
252 	else
253 		errx(1, "%s: bad value", s);
254 }
255 
256 #else
257 
258 static struct in_px *sintab_nl[] = {
259 	&in_del.addr,		/* RIDADDR */
260 	&in_add.addr,		/* ADDR */
261 	NULL,			/* MASK */
262 	&in_add.dst_addr,	/* DSTADDR*/
263 	&in_add.brd_addr,	/* BRDADDR*/
264 };
265 
266 static void
267 in_getip(const char *addr_str, struct in_addr *ip)
268 {
269 	struct hostent *hp;
270 	struct netent *np;
271 
272 	if (inet_aton(addr_str, ip))
273 		return;
274 	if ((hp = gethostbyname(addr_str)) != NULL)
275 		bcopy(hp->h_addr, (char *)ip,
276 		    MIN((size_t)hp->h_length, sizeof(ip)));
277 	else if ((np = getnetbyname(addr_str)) != NULL)
278 		*ip = inet_makeaddr(np->n_net, INADDR_ANY);
279 	else
280 		errx(1, "%s: bad value", addr_str);
281 }
282 
283 static void
284 in_getaddr(const char *s, int which)
285 {
286         struct in_px *px = sintab_nl[which];
287 
288 	if (which == MASK) {
289 		struct in_px *px_addr = sintab_nl[ADDR];
290 		struct in_addr mask = {};
291 
292 		in_getip(s, &mask);
293 		px_addr->plen = __bitcount32(mask.s_addr);
294 		px_addr->maskset = true;
295 		return;
296 	}
297 
298 	if (which == ADDR) {
299 		char *p = NULL;
300 
301 		if((p = strrchr(s, '/')) != NULL) {
302 			const char *errstr;
303 			/* address is `name/masklen' */
304 			int masklen;
305 			*p = '\0';
306 			if (!isdigit(*(p + 1)))
307 				errstr = "invalid";
308 			else
309 				masklen = (int)strtonum(p + 1, 0, 32, &errstr);
310 			if (errstr != NULL) {
311 				*p = '/';
312 				errx(1, "%s: bad value (width %s)", s, errstr);
313 			}
314 			px->plen = masklen;
315 			px->maskset = true;
316 		}
317 	}
318 
319 	in_getip(s, &px->addr);
320 	px->addrset = true;
321 }
322 
323 
324 static int
325 in_exec_nl(struct io_handler *h, int action, void *data)
326 {
327 	struct in_pdata *pdata = (struct in_pdata *)data;
328 	struct snl_writer nw = {};
329 
330 	snl_init_writer(h->ss, &nw);
331 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, action);
332 	struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
333 
334 	ifahdr->ifa_family = AF_INET;
335 	ifahdr->ifa_prefixlen = pdata->addr.plen;
336 	ifahdr->ifa_index = if_nametoindex_nl(h->ss, name);
337 
338 	snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr);
339 	if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset)
340 		snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr);
341 	if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset)
342 		snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr);
343 
344 	int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD);
345 	snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags);
346 	if (pdata->vhid != 0)
347 		snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid);
348 	snl_end_attr_nested(&nw, off);
349 
350 	if (!snl_finalize_msg(&nw) || !snl_send_message(h->ss, hdr))
351 		return (0);
352 
353 	struct snl_errmsg_data e = {};
354 	snl_read_reply_code(h->ss, hdr->nlmsg_seq, &e);
355 	if (e.error_str != NULL)
356 		warnx("%s(): %s", __func__, e.error_str);
357 
358 	return (e.error);
359 }
360 
361 static void
362 in_setdefaultmask_nl(void)
363 {
364         struct in_px *px = sintab_nl[ADDR];
365 
366 	in_addr_t i = ntohl(px->addr.s_addr);
367 
368 	/*
369 	 * If netmask isn't supplied, use historical default.
370 	 * This is deprecated for interfaces other than loopback
371 	 * or point-to-point; warn in other cases.  In the future
372 	 * we should return an error rather than warning.
373 	 */
374 	if (IN_CLASSA(i))
375 		px->plen = IN_CLASSA_NSHIFT;
376 	else if (IN_CLASSB(i))
377 		px->plen = IN_CLASSB_NSHIFT;
378 	else
379 		px->plen = IN_CLASSC_NSHIFT;
380 	px->maskset = true;
381 }
382 #endif
383 
384 static void
385 warn_nomask(int ifflags)
386 {
387 	if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
388 		warnx("WARNING: setting interface address without mask "
389 		    "is deprecated,\ndefault mask may not be correct.");
390 	}
391 }
392 
393 static void
394 in_postproc(int s, const struct afswtch *afp, int newaddr, int ifflags)
395 {
396 #ifdef WITHOUT_NETLINK
397 	if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) {
398 		warn_nomask(ifflags);
399 	}
400 #else
401 	if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) {
402 		warn_nomask(ifflags);
403 	    in_setdefaultmask_nl();
404 	}
405 #endif
406 }
407 
408 static void
409 in_status_tunnel(int s)
410 {
411 	char src[NI_MAXHOST];
412 	char dst[NI_MAXHOST];
413 	struct ifreq ifr;
414 	const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr;
415 
416 	memset(&ifr, 0, sizeof(ifr));
417 	strlcpy(ifr.ifr_name, name, IFNAMSIZ);
418 
419 	if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0)
420 		return;
421 	if (sa->sa_family != AF_INET)
422 		return;
423 	if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0)
424 		src[0] = '\0';
425 
426 	if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0)
427 		return;
428 	if (sa->sa_family != AF_INET)
429 		return;
430 	if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0)
431 		dst[0] = '\0';
432 
433 	printf("\ttunnel inet %s --> %s\n", src, dst);
434 }
435 
436 static void
437 in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
438 {
439 	struct in_aliasreq addreq;
440 
441 	memset(&addreq, 0, sizeof(addreq));
442 	strlcpy(addreq.ifra_name, name, IFNAMSIZ);
443 	memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
444 	memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len);
445 
446 	if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0)
447 		warn("SIOCSIFPHYADDR");
448 }
449 
450 static void
451 in_set_vhid(int vhid)
452 {
453 #ifdef WITHOUT_NETLINK
454 	in_addreq.ifra_vhid = vhid;
455 #else
456 	in_add.vhid = (uint32_t)vhid;
457 #endif
458 }
459 
460 static struct afswtch af_inet = {
461 	.af_name	= "inet",
462 	.af_af		= AF_INET,
463 #ifdef WITHOUT_NETLINK
464 	.af_status	= in_status,
465 #else
466 	.af_status_nl	= in_status_nl,
467 #endif
468 	.af_getaddr	= in_getaddr,
469 	.af_postproc	= in_postproc,
470 	.af_status_tunnel = in_status_tunnel,
471 	.af_settunnel	= in_set_tunnel,
472 	.af_setvhid	= in_set_vhid,
473 #ifdef WITHOUT_NETLINK
474 	.af_difaddr	= SIOCDIFADDR,
475 	.af_aifaddr	= SIOCAIFADDR,
476 	.af_ridreq	= &in_ridreq,
477 	.af_addreq	= &in_addreq,
478 	.af_exec	= af_exec_ioctl,
479 #else
480 	.af_difaddr	= NL_RTM_DELADDR,
481 	.af_aifaddr	= NL_RTM_NEWADDR,
482 	.af_ridreq	= &in_del,
483 	.af_addreq	= &in_add,
484 	.af_exec	= in_exec_nl,
485 #endif
486 };
487 
488 static __constructor void
489 inet_ctor(void)
490 {
491 
492 #ifndef RESCUE
493 	if (!feature_present("inet"))
494 		return;
495 #endif
496 	af_register(&af_inet);
497 }
498