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 #endif 440 441 static void 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 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 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 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 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 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