1 /*- 2 * Copyright (c) 2016 Yandex LLC 3 * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/counter.h> 34 #include <sys/eventhandler.h> 35 #include <sys/errno.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/mbuf.h> 40 #include <sys/module.h> 41 #include <sys/rmlock.h> 42 #include <sys/rwlock.h> 43 #include <sys/socket.h> 44 #include <sys/queue.h> 45 #include <sys/syslog.h> 46 #include <sys/sysctl.h> 47 48 #include <net/if.h> 49 #include <net/if_var.h> 50 #include <net/if_private.h> 51 #include <net/netisr.h> 52 #include <net/pfil.h> 53 #include <net/vnet.h> 54 55 #include <netinet/in.h> 56 #include <netinet/ip_var.h> 57 #include <netinet/ip_fw.h> 58 #include <netinet/ip6.h> 59 #include <netinet/icmp6.h> 60 #include <netinet6/in6_var.h> 61 #include <netinet6/ip6_var.h> 62 63 #include <netpfil/ipfw/ip_fw_private.h> 64 #include <netpfil/ipfw/nptv6/nptv6.h> 65 66 VNET_DEFINE_STATIC(uint16_t, nptv6_eid) = 0; 67 #define V_nptv6_eid VNET(nptv6_eid) 68 #define IPFW_TLV_NPTV6_NAME IPFW_TLV_EACTION_NAME(V_nptv6_eid) 69 70 static eventhandler_tag nptv6_ifaddr_event; 71 72 static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set); 73 static void nptv6_free_config(struct nptv6_cfg *cfg); 74 static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni, 75 const char *name, uint8_t set); 76 static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, 77 int offset); 78 static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, 79 int offset); 80 81 #define NPTV6_LOOKUP(chain, cmd) \ 82 (struct nptv6_cfg *)SRV_OBJECT((chain), (cmd)->arg1) 83 84 #ifndef IN6_MASK_ADDR 85 #define IN6_MASK_ADDR(a, m) do { \ 86 (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ 87 (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ 88 (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ 89 (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ 90 } while (0) 91 #endif 92 #ifndef IN6_ARE_MASKED_ADDR_EQUAL 93 #define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ 94 (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ 95 (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ 96 (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ 97 (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) 98 #endif 99 100 #if 0 101 #define NPTV6_DEBUG(fmt, ...) do { \ 102 printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ 103 } while (0) 104 #define NPTV6_IPDEBUG(fmt, ...) do { \ 105 char _s[INET6_ADDRSTRLEN], _d[INET6_ADDRSTRLEN]; \ 106 printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ 107 } while (0) 108 #else 109 #define NPTV6_DEBUG(fmt, ...) 110 #define NPTV6_IPDEBUG(fmt, ...) 111 #endif 112 113 static int 114 nptv6_getlasthdr(struct nptv6_cfg *cfg, struct mbuf *m, int *offset) 115 { 116 struct ip6_hdr *ip6; 117 struct ip6_hbh *hbh; 118 int proto, hlen; 119 120 hlen = (offset == NULL) ? 0: *offset; 121 if (m->m_len < hlen) 122 return (-1); 123 ip6 = mtodo(m, hlen); 124 hlen += sizeof(*ip6); 125 proto = ip6->ip6_nxt; 126 while (proto == IPPROTO_HOPOPTS || proto == IPPROTO_ROUTING || 127 proto == IPPROTO_DSTOPTS) { 128 hbh = mtodo(m, hlen); 129 if (m->m_len < hlen) 130 return (-1); 131 proto = hbh->ip6h_nxt; 132 hlen += (hbh->ip6h_len + 1) << 3; 133 } 134 if (offset != NULL) 135 *offset = hlen; 136 return (proto); 137 } 138 139 static int 140 nptv6_translate_icmpv6(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) 141 { 142 struct icmp6_hdr *icmp6; 143 struct ip6_hdr *ip6; 144 struct mbuf *m; 145 146 m = *mp; 147 if (offset > m->m_len) 148 return (-1); 149 icmp6 = mtodo(m, offset); 150 NPTV6_DEBUG("ICMPv6 type %d", icmp6->icmp6_type); 151 switch (icmp6->icmp6_type) { 152 case ICMP6_DST_UNREACH: 153 case ICMP6_PACKET_TOO_BIG: 154 case ICMP6_TIME_EXCEEDED: 155 case ICMP6_PARAM_PROB: 156 break; 157 case ICMP6_ECHO_REQUEST: 158 case ICMP6_ECHO_REPLY: 159 /* nothing to translate */ 160 return (0); 161 default: 162 /* 163 * XXX: We can add some checks to not translate NDP and MLD 164 * messages. Currently user must explicitly allow these message 165 * types, otherwise packets will be dropped. 166 */ 167 return (-1); 168 } 169 offset += sizeof(*icmp6); 170 if (offset + sizeof(*ip6) > m->m_pkthdr.len) 171 return (-1); 172 if (offset + sizeof(*ip6) > m->m_len) 173 *mp = m = m_pullup(m, offset + sizeof(*ip6)); 174 if (m == NULL) 175 return (-1); 176 ip6 = mtodo(m, offset); 177 NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, 178 inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), 179 inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), 180 ip6->ip6_nxt); 181 if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src, 182 &cfg->external, &cfg->mask)) 183 return (nptv6_rewrite_external(cfg, mp, offset)); 184 else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, 185 &cfg->internal, &cfg->mask)) 186 return (nptv6_rewrite_internal(cfg, mp, offset)); 187 /* 188 * Addresses in the inner IPv6 header doesn't matched to 189 * our prefixes. 190 */ 191 return (-1); 192 } 193 194 static int 195 nptv6_search_index(struct nptv6_cfg *cfg, struct in6_addr *a) 196 { 197 int idx; 198 199 if (cfg->flags & NPTV6_48PLEN) 200 return (3); 201 202 /* Search suitable word index for adjustment */ 203 for (idx = 4; idx < 8; idx++) 204 if (a->s6_addr16[idx] != 0xffff) 205 break; 206 /* 207 * RFC 6296 p3.7: If an NPTv6 Translator discovers a datagram with 208 * an IID of all-zeros while performing address mapping, that 209 * datagram MUST be dropped, and an ICMPv6 Parameter Problem error 210 * SHOULD be generated. 211 */ 212 if (idx == 8 || 213 (a->s6_addr32[2] == 0 && a->s6_addr32[3] == 0)) 214 return (-1); 215 return (idx); 216 } 217 218 static void 219 nptv6_copy_addr(struct in6_addr *src, struct in6_addr *dst, 220 struct in6_addr *mask) 221 { 222 int i; 223 224 for (i = 0; i < 8 && mask->s6_addr8[i] != 0; i++) { 225 dst->s6_addr8[i] &= ~mask->s6_addr8[i]; 226 dst->s6_addr8[i] |= src->s6_addr8[i] & mask->s6_addr8[i]; 227 } 228 } 229 230 static int 231 nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) 232 { 233 struct in6_addr *addr; 234 struct ip6_hdr *ip6; 235 int idx, proto; 236 uint16_t adj; 237 238 ip6 = mtodo(*mp, offset); 239 NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, 240 inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), 241 inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), 242 ip6->ip6_nxt); 243 if (offset == 0) 244 addr = &ip6->ip6_src; 245 else { 246 /* 247 * When we rewriting inner IPv6 header, we need to rewrite 248 * destination address back to external prefix. The datagram in 249 * the ICMPv6 payload should looks like it was send from 250 * external prefix. 251 */ 252 addr = &ip6->ip6_dst; 253 } 254 idx = nptv6_search_index(cfg, addr); 255 if (idx < 0) { 256 /* 257 * Do not send ICMPv6 error when offset isn't zero. 258 * This means we are rewriting inner IPv6 header in the 259 * ICMPv6 error message. 260 */ 261 if (offset == 0) { 262 icmp6_error2(*mp, ICMP6_DST_UNREACH, 263 ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif); 264 *mp = NULL; 265 } 266 return (IP_FW_DENY); 267 } 268 adj = addr->s6_addr16[idx]; 269 nptv6_copy_addr(&cfg->external, addr, &cfg->mask); 270 adj = cksum_add(adj, cfg->adjustment); 271 if (adj == 0xffff) 272 adj = 0; 273 addr->s6_addr16[idx] = adj; 274 if (offset == 0) { 275 /* 276 * We may need to translate addresses in the inner IPv6 277 * header for ICMPv6 error messages. 278 */ 279 proto = nptv6_getlasthdr(cfg, *mp, &offset); 280 if (proto < 0 || (proto == IPPROTO_ICMPV6 && 281 nptv6_translate_icmpv6(cfg, mp, offset) != 0)) 282 return (IP_FW_DENY); 283 NPTV6STAT_INC(cfg, in2ex); 284 } 285 return (0); 286 } 287 288 static int 289 nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp, int offset) 290 { 291 struct in6_addr *addr; 292 struct ip6_hdr *ip6; 293 int idx, proto; 294 uint16_t adj; 295 296 ip6 = mtodo(*mp, offset); 297 NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset, 298 inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), 299 inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), 300 ip6->ip6_nxt); 301 if (offset == 0) 302 addr = &ip6->ip6_dst; 303 else { 304 /* 305 * When we rewriting inner IPv6 header, we need to rewrite 306 * source address back to internal prefix. The datagram in 307 * the ICMPv6 payload should looks like it was send from 308 * internal prefix. 309 */ 310 addr = &ip6->ip6_src; 311 } 312 idx = nptv6_search_index(cfg, addr); 313 if (idx < 0) { 314 /* 315 * Do not send ICMPv6 error when offset isn't zero. 316 * This means we are rewriting inner IPv6 header in the 317 * ICMPv6 error message. 318 */ 319 if (offset == 0) { 320 icmp6_error2(*mp, ICMP6_DST_UNREACH, 321 ICMP6_DST_UNREACH_ADDR, 0, (*mp)->m_pkthdr.rcvif); 322 *mp = NULL; 323 } 324 return (IP_FW_DENY); 325 } 326 adj = addr->s6_addr16[idx]; 327 nptv6_copy_addr(&cfg->internal, addr, &cfg->mask); 328 adj = cksum_add(adj, ~cfg->adjustment); 329 if (adj == 0xffff) 330 adj = 0; 331 addr->s6_addr16[idx] = adj; 332 if (offset == 0) { 333 /* 334 * We may need to translate addresses in the inner IPv6 335 * header for ICMPv6 error messages. 336 */ 337 proto = nptv6_getlasthdr(cfg, *mp, &offset); 338 if (proto < 0 || (proto == IPPROTO_ICMPV6 && 339 nptv6_translate_icmpv6(cfg, mp, offset) != 0)) 340 return (IP_FW_DENY); 341 NPTV6STAT_INC(cfg, ex2in); 342 } 343 return (0); 344 } 345 346 /* 347 * ipfw external action handler. 348 */ 349 static int 350 ipfw_nptv6(struct ip_fw_chain *chain, struct ip_fw_args *args, 351 ipfw_insn *cmd, int *done) 352 { 353 struct ip6_hdr *ip6; 354 struct nptv6_cfg *cfg; 355 ipfw_insn *icmd; 356 int ret; 357 358 *done = 0; /* try next rule if not matched */ 359 ret = IP_FW_DENY; 360 icmd = cmd + 1; 361 if (cmd->opcode != O_EXTERNAL_ACTION || 362 cmd->arg1 != V_nptv6_eid || 363 icmd->opcode != O_EXTERNAL_INSTANCE || 364 (cfg = NPTV6_LOOKUP(chain, icmd)) == NULL || 365 (cfg->flags & NPTV6_READY) == 0) 366 return (ret); 367 /* 368 * We need act as router, so when forwarding is disabled - 369 * do nothing. 370 */ 371 if (V_ip6_forwarding == 0 || args->f_id.addr_type != 6) 372 return (ret); 373 /* 374 * NOTE: we expect ipfw_chk() did m_pullup() up to upper level 375 * protocol's headers. Also we skip some checks, that ip6_input(), 376 * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. 377 */ 378 ip6 = mtod(args->m, struct ip6_hdr *); 379 NPTV6_IPDEBUG("eid %u, oid %u, %s -> %s %d", 380 cmd->arg1, icmd->arg1, 381 inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)), 382 inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)), 383 ip6->ip6_nxt); 384 if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src, 385 &cfg->internal, &cfg->mask)) { 386 /* 387 * XXX: Do not translate packets when both src and dst 388 * are from internal prefix. 389 */ 390 if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, 391 &cfg->internal, &cfg->mask)) 392 return (ret); 393 ret = nptv6_rewrite_internal(cfg, &args->m, 0); 394 } else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst, 395 &cfg->external, &cfg->mask)) 396 ret = nptv6_rewrite_external(cfg, &args->m, 0); 397 else 398 return (ret); 399 /* 400 * If address wasn't rewrited - free mbuf and terminate the search. 401 */ 402 if (ret != 0) { 403 if (args->m != NULL) { 404 m_freem(args->m); 405 args->m = NULL; /* mark mbuf as consumed */ 406 } 407 NPTV6STAT_INC(cfg, dropped); 408 *done = 1; 409 } else { 410 /* Terminate the search if one_pass is set */ 411 *done = V_fw_one_pass; 412 /* Update args->f_id when one_pass is off */ 413 if (*done == 0) { 414 ip6 = mtod(args->m, struct ip6_hdr *); 415 args->f_id.src_ip6 = ip6->ip6_src; 416 args->f_id.dst_ip6 = ip6->ip6_dst; 417 } 418 } 419 return (ret); 420 } 421 422 static struct nptv6_cfg * 423 nptv6_alloc_config(const char *name, uint8_t set) 424 { 425 struct nptv6_cfg *cfg; 426 427 cfg = malloc(sizeof(struct nptv6_cfg), M_IPFW, M_WAITOK | M_ZERO); 428 COUNTER_ARRAY_ALLOC(cfg->stats, NPTV6STATS, M_WAITOK); 429 cfg->no.name = cfg->name; 430 cfg->no.etlv = IPFW_TLV_NPTV6_NAME; 431 cfg->no.set = set; 432 strlcpy(cfg->name, name, sizeof(cfg->name)); 433 return (cfg); 434 } 435 436 static void 437 nptv6_free_config(struct nptv6_cfg *cfg) 438 { 439 440 COUNTER_ARRAY_FREE(cfg->stats, NPTV6STATS); 441 free(cfg, M_IPFW); 442 } 443 444 static void 445 nptv6_export_config(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, 446 ipfw_nptv6_cfg *uc) 447 { 448 449 uc->internal = cfg->internal; 450 if (cfg->flags & NPTV6_DYNAMIC_PREFIX) 451 memcpy(uc->if_name, cfg->if_name, IF_NAMESIZE); 452 else 453 uc->external = cfg->external; 454 uc->plen = cfg->plen; 455 uc->flags = cfg->flags & NPTV6_FLAGSMASK; 456 uc->set = cfg->no.set; 457 strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); 458 } 459 460 struct nptv6_dump_arg { 461 struct ip_fw_chain *ch; 462 struct sockopt_data *sd; 463 }; 464 465 static int 466 export_config_cb(struct namedobj_instance *ni, struct named_object *no, 467 void *arg) 468 { 469 struct nptv6_dump_arg *da = (struct nptv6_dump_arg *)arg; 470 ipfw_nptv6_cfg *uc; 471 472 uc = (ipfw_nptv6_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc)); 473 nptv6_export_config(da->ch, (struct nptv6_cfg *)no, uc); 474 return (0); 475 } 476 477 static struct nptv6_cfg * 478 nptv6_find(struct namedobj_instance *ni, const char *name, uint8_t set) 479 { 480 struct nptv6_cfg *cfg; 481 482 cfg = (struct nptv6_cfg *)ipfw_objhash_lookup_name_type(ni, set, 483 IPFW_TLV_NPTV6_NAME, name); 484 485 return (cfg); 486 } 487 488 static void 489 nptv6_calculate_adjustment(struct nptv6_cfg *cfg) 490 { 491 uint16_t i, e; 492 uint16_t *p; 493 494 /* Calculate checksum of internal prefix */ 495 for (i = 0, p = (uint16_t *)&cfg->internal; 496 p < (uint16_t *)(&cfg->internal + 1); p++) 497 i = cksum_add(i, *p); 498 499 /* Calculate checksum of external prefix */ 500 for (e = 0, p = (uint16_t *)&cfg->external; 501 p < (uint16_t *)(&cfg->external + 1); p++) 502 e = cksum_add(e, *p); 503 504 /* Adjustment value for Int->Ext direction */ 505 cfg->adjustment = cksum_add(~e, i); 506 } 507 508 static int 509 nptv6_check_prefix(const struct in6_addr *addr) 510 { 511 512 if (IN6_IS_ADDR_MULTICAST(addr) || 513 IN6_IS_ADDR_LINKLOCAL(addr) || 514 IN6_IS_ADDR_LOOPBACK(addr) || 515 IN6_IS_ADDR_UNSPECIFIED(addr)) 516 return (EINVAL); 517 return (0); 518 } 519 520 static void 521 nptv6_set_external(struct nptv6_cfg *cfg, struct in6_addr *addr) 522 { 523 524 cfg->external = *addr; 525 IN6_MASK_ADDR(&cfg->external, &cfg->mask); 526 nptv6_calculate_adjustment(cfg); 527 cfg->flags |= NPTV6_READY; 528 } 529 530 /* 531 * Try to determine what prefix to use as external for 532 * configured interface name. 533 */ 534 static void 535 nptv6_find_prefix(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, 536 struct ifnet *ifp) 537 { 538 struct epoch_tracker et; 539 struct ifaddr *ifa; 540 struct in6_ifaddr *ia; 541 542 MPASS(cfg->flags & NPTV6_DYNAMIC_PREFIX); 543 IPFW_UH_WLOCK_ASSERT(ch); 544 545 if (ifp == NULL) { 546 ifp = ifunit_ref(cfg->if_name); 547 if (ifp == NULL) 548 return; 549 } 550 NET_EPOCH_ENTER(et); 551 CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 552 if (ifa->ifa_addr->sa_family != AF_INET6) 553 continue; 554 ia = (struct in6_ifaddr *)ifa; 555 if (nptv6_check_prefix(&ia->ia_addr.sin6_addr) || 556 IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr, 557 &cfg->internal, &cfg->mask)) 558 continue; 559 /* Suitable address is found. */ 560 nptv6_set_external(cfg, &ia->ia_addr.sin6_addr); 561 break; 562 } 563 NET_EPOCH_EXIT(et); 564 if_rele(ifp); 565 } 566 567 struct ifaddr_event_args { 568 struct ifnet *ifp; 569 const struct in6_addr *addr; 570 int event; 571 }; 572 573 static int 574 ifaddr_cb(struct namedobj_instance *ni, struct named_object *no, 575 void *arg) 576 { 577 struct ifaddr_event_args *args; 578 struct ip_fw_chain *ch; 579 struct nptv6_cfg *cfg; 580 581 ch = &V_layer3_chain; 582 cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx); 583 if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0) 584 return (0); 585 586 args = arg; 587 /* If interface name doesn't match, ignore */ 588 if (strncmp(args->ifp->if_xname, cfg->if_name, IF_NAMESIZE)) 589 return (0); 590 if (args->ifp->if_flags & IFF_DYING) { /* XXX: is it possible? */ 591 cfg->flags &= ~NPTV6_READY; 592 return (0); 593 } 594 if (args->event == IFADDR_EVENT_DEL) { 595 /* If instance is not ready, ignore */ 596 if ((cfg->flags & NPTV6_READY) == 0) 597 return (0); 598 /* If address does not match the external prefix, ignore */ 599 if (IN6_ARE_MASKED_ADDR_EQUAL(&cfg->external, args->addr, 600 &cfg->mask) != 0) 601 return (0); 602 /* Otherwise clear READY flag */ 603 cfg->flags &= ~NPTV6_READY; 604 } else {/* IFADDR_EVENT_ADD */ 605 /* If instance is already ready, ignore */ 606 if (cfg->flags & NPTV6_READY) 607 return (0); 608 /* If address is not suitable for prefix, ignore */ 609 if (nptv6_check_prefix(args->addr) || 610 IN6_ARE_MASKED_ADDR_EQUAL(args->addr, &cfg->internal, 611 &cfg->mask)) 612 return (0); 613 /* FALLTHROUGH */ 614 } 615 MPASS(!(cfg->flags & NPTV6_READY)); 616 /* Try to determine the prefix */ 617 if_ref(args->ifp); 618 nptv6_find_prefix(ch, cfg, args->ifp); 619 return (0); 620 } 621 622 static void 623 nptv6_ifaddrevent_handler(void *arg __unused, struct ifnet *ifp, 624 struct ifaddr *ifa, int event) 625 { 626 struct ifaddr_event_args args; 627 struct ip_fw_chain *ch; 628 629 if (ifa->ifa_addr->sa_family != AF_INET6) 630 return; 631 632 args.ifp = ifp; 633 args.addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; 634 args.event = event; 635 636 ch = &V_layer3_chain; 637 IPFW_UH_WLOCK(ch); 638 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), ifaddr_cb, &args, 639 IPFW_TLV_NPTV6_NAME); 640 IPFW_UH_WUNLOCK(ch); 641 } 642 643 /* 644 * Creates new NPTv6 instance. 645 * Data layout (v0)(current): 646 * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ] 647 * 648 * Returns 0 on success 649 */ 650 static int 651 nptv6_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 652 struct sockopt_data *sd) 653 { 654 struct in6_addr mask; 655 ipfw_obj_lheader *olh; 656 ipfw_nptv6_cfg *uc; 657 struct namedobj_instance *ni; 658 struct nptv6_cfg *cfg; 659 660 if (sd->valsize != sizeof(*olh) + sizeof(*uc)) 661 return (EINVAL); 662 663 olh = (ipfw_obj_lheader *)sd->kbuf; 664 uc = (ipfw_nptv6_cfg *)(olh + 1); 665 if (ipfw_check_object_name_generic(uc->name) != 0) 666 return (EINVAL); 667 if (uc->plen < 8 || uc->plen > 64 || uc->set >= IPFW_MAX_SETS) 668 return (EINVAL); 669 if (nptv6_check_prefix(&uc->internal)) 670 return (EINVAL); 671 in6_prefixlen2mask(&mask, uc->plen); 672 if ((uc->flags & NPTV6_DYNAMIC_PREFIX) == 0 && ( 673 nptv6_check_prefix(&uc->external) || 674 IN6_ARE_MASKED_ADDR_EQUAL(&uc->external, &uc->internal, &mask))) 675 return (EINVAL); 676 677 ni = CHAIN_TO_SRV(ch); 678 IPFW_UH_RLOCK(ch); 679 if (nptv6_find(ni, uc->name, uc->set) != NULL) { 680 IPFW_UH_RUNLOCK(ch); 681 return (EEXIST); 682 } 683 IPFW_UH_RUNLOCK(ch); 684 685 cfg = nptv6_alloc_config(uc->name, uc->set); 686 cfg->plen = uc->plen; 687 cfg->flags = uc->flags & NPTV6_FLAGSMASK; 688 if (cfg->plen <= 48) 689 cfg->flags |= NPTV6_48PLEN; 690 cfg->mask = mask; 691 cfg->internal = uc->internal; 692 IN6_MASK_ADDR(&cfg->internal, &mask); 693 if (cfg->flags & NPTV6_DYNAMIC_PREFIX) 694 memcpy(cfg->if_name, uc->if_name, IF_NAMESIZE); 695 else 696 nptv6_set_external(cfg, &uc->external); 697 698 if ((uc->flags & NPTV6_DYNAMIC_PREFIX) != 0 && 699 nptv6_ifaddr_event == NULL) 700 nptv6_ifaddr_event = EVENTHANDLER_REGISTER( 701 ifaddr_event_ext, nptv6_ifaddrevent_handler, NULL, 702 EVENTHANDLER_PRI_ANY); 703 704 IPFW_UH_WLOCK(ch); 705 if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) { 706 IPFW_UH_WUNLOCK(ch); 707 nptv6_free_config(cfg); 708 return (ENOSPC); 709 } 710 ipfw_objhash_add(ni, &cfg->no); 711 SRV_OBJECT(ch, cfg->no.kidx) = cfg; 712 if (cfg->flags & NPTV6_DYNAMIC_PREFIX) 713 nptv6_find_prefix(ch, cfg, NULL); 714 IPFW_UH_WUNLOCK(ch); 715 716 return (0); 717 } 718 719 /* 720 * Destroys NPTv6 instance. 721 * Data layout (v0)(current): 722 * Request: [ ipfw_obj_header ] 723 * 724 * Returns 0 on success 725 */ 726 static int 727 nptv6_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 728 struct sockopt_data *sd) 729 { 730 ipfw_obj_header *oh; 731 struct nptv6_cfg *cfg; 732 733 if (sd->valsize != sizeof(*oh)) 734 return (EINVAL); 735 736 oh = (ipfw_obj_header *)sd->kbuf; 737 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0) 738 return (EINVAL); 739 740 IPFW_UH_WLOCK(ch); 741 cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 742 if (cfg == NULL) { 743 IPFW_UH_WUNLOCK(ch); 744 return (ESRCH); 745 } 746 if (cfg->no.refcnt > 0) { 747 IPFW_UH_WUNLOCK(ch); 748 return (EBUSY); 749 } 750 751 ipfw_reset_eaction_instance(ch, V_nptv6_eid, cfg->no.kidx); 752 SRV_OBJECT(ch, cfg->no.kidx) = NULL; 753 ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no); 754 ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx); 755 IPFW_UH_WUNLOCK(ch); 756 757 nptv6_free_config(cfg); 758 return (0); 759 } 760 761 /* 762 * Get or change nptv6 instance config. 763 * Request: [ ipfw_obj_header [ ipfw_nptv6_cfg ] ] 764 */ 765 static int 766 nptv6_config(struct ip_fw_chain *chain, ip_fw3_opheader *op, 767 struct sockopt_data *sd) 768 { 769 770 return (EOPNOTSUPP); 771 } 772 773 /* 774 * Lists all NPTv6 instances currently available in kernel. 775 * Data layout (v0)(current): 776 * Request: [ ipfw_obj_lheader ] 777 * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ] 778 * 779 * Returns 0 on success 780 */ 781 static int 782 nptv6_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 783 struct sockopt_data *sd) 784 { 785 ipfw_obj_lheader *olh; 786 struct nptv6_dump_arg da; 787 788 /* Check minimum header size */ 789 if (sd->valsize < sizeof(ipfw_obj_lheader)) 790 return (EINVAL); 791 792 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 793 794 IPFW_UH_RLOCK(ch); 795 olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), 796 IPFW_TLV_NPTV6_NAME); 797 olh->objsize = sizeof(ipfw_nptv6_cfg); 798 olh->size = sizeof(*olh) + olh->count * olh->objsize; 799 800 if (sd->valsize < olh->size) { 801 IPFW_UH_RUNLOCK(ch); 802 return (ENOMEM); 803 } 804 memset(&da, 0, sizeof(da)); 805 da.ch = ch; 806 da.sd = sd; 807 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, 808 &da, IPFW_TLV_NPTV6_NAME); 809 IPFW_UH_RUNLOCK(ch); 810 811 return (0); 812 } 813 814 #define __COPY_STAT_FIELD(_cfg, _stats, _field) \ 815 (_stats)->_field = NPTV6STAT_FETCH(_cfg, _field) 816 static void 817 export_stats(struct ip_fw_chain *ch, struct nptv6_cfg *cfg, 818 struct ipfw_nptv6_stats *stats) 819 { 820 821 __COPY_STAT_FIELD(cfg, stats, in2ex); 822 __COPY_STAT_FIELD(cfg, stats, ex2in); 823 __COPY_STAT_FIELD(cfg, stats, dropped); 824 } 825 826 /* 827 * Get NPTv6 statistics. 828 * Data layout (v0)(current): 829 * Request: [ ipfw_obj_header ] 830 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]] 831 * 832 * Returns 0 on success 833 */ 834 static int 835 nptv6_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 836 struct sockopt_data *sd) 837 { 838 struct ipfw_nptv6_stats stats; 839 struct nptv6_cfg *cfg; 840 ipfw_obj_header *oh; 841 ipfw_obj_ctlv *ctlv; 842 size_t sz; 843 844 sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); 845 if (sd->valsize % sizeof(uint64_t)) 846 return (EINVAL); 847 if (sd->valsize < sz) 848 return (ENOMEM); 849 oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 850 if (oh == NULL) 851 return (EINVAL); 852 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 853 oh->ntlv.set >= IPFW_MAX_SETS) 854 return (EINVAL); 855 memset(&stats, 0, sizeof(stats)); 856 857 IPFW_UH_RLOCK(ch); 858 cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 859 if (cfg == NULL) { 860 IPFW_UH_RUNLOCK(ch); 861 return (ESRCH); 862 } 863 export_stats(ch, cfg, &stats); 864 IPFW_UH_RUNLOCK(ch); 865 866 ctlv = (ipfw_obj_ctlv *)(oh + 1); 867 memset(ctlv, 0, sizeof(*ctlv)); 868 ctlv->head.type = IPFW_TLV_COUNTERS; 869 ctlv->head.length = sz - sizeof(ipfw_obj_header); 870 ctlv->count = sizeof(stats) / sizeof(uint64_t); 871 ctlv->objsize = sizeof(uint64_t); 872 ctlv->version = 1; 873 memcpy(ctlv + 1, &stats, sizeof(stats)); 874 return (0); 875 } 876 877 /* 878 * Reset NPTv6 statistics. 879 * Data layout (v0)(current): 880 * Request: [ ipfw_obj_header ] 881 * 882 * Returns 0 on success 883 */ 884 static int 885 nptv6_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, 886 struct sockopt_data *sd) 887 { 888 struct nptv6_cfg *cfg; 889 ipfw_obj_header *oh; 890 891 if (sd->valsize != sizeof(*oh)) 892 return (EINVAL); 893 oh = (ipfw_obj_header *)sd->kbuf; 894 if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || 895 oh->ntlv.set >= IPFW_MAX_SETS) 896 return (EINVAL); 897 898 IPFW_UH_WLOCK(ch); 899 cfg = nptv6_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); 900 if (cfg == NULL) { 901 IPFW_UH_WUNLOCK(ch); 902 return (ESRCH); 903 } 904 COUNTER_ARRAY_ZERO(cfg->stats, NPTV6STATS); 905 IPFW_UH_WUNLOCK(ch); 906 return (0); 907 } 908 909 static struct ipfw_sopt_handler scodes[] = { 910 { IP_FW_NPTV6_CREATE, 0, HDIR_SET, nptv6_create }, 911 { IP_FW_NPTV6_DESTROY,0, HDIR_SET, nptv6_destroy }, 912 { IP_FW_NPTV6_CONFIG, 0, HDIR_BOTH, nptv6_config }, 913 { IP_FW_NPTV6_LIST, 0, HDIR_GET, nptv6_list }, 914 { IP_FW_NPTV6_STATS, 0, HDIR_GET, nptv6_stats }, 915 { IP_FW_NPTV6_RESET_STATS,0, HDIR_SET, nptv6_reset_stats }, 916 }; 917 918 static int 919 nptv6_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 920 { 921 ipfw_insn *icmd; 922 923 icmd = cmd - 1; 924 NPTV6_DEBUG("opcode %d, arg1 %d, opcode0 %d, arg1 %d", 925 cmd->opcode, cmd->arg1, icmd->opcode, icmd->arg1); 926 if (icmd->opcode != O_EXTERNAL_ACTION || 927 icmd->arg1 != V_nptv6_eid) 928 return (1); 929 930 *puidx = cmd->arg1; 931 *ptype = 0; 932 return (0); 933 } 934 935 static void 936 nptv6_update_arg1(ipfw_insn *cmd, uint16_t idx) 937 { 938 939 cmd->arg1 = idx; 940 NPTV6_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1); 941 } 942 943 static int 944 nptv6_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 945 struct named_object **pno) 946 { 947 int err; 948 949 err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti, 950 IPFW_TLV_NPTV6_NAME, pno); 951 NPTV6_DEBUG("uidx %u, type %u, err %d", ti->uidx, ti->type, err); 952 return (err); 953 } 954 955 static struct named_object * 956 nptv6_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 957 { 958 struct namedobj_instance *ni; 959 struct named_object *no; 960 961 IPFW_UH_WLOCK_ASSERT(ch); 962 ni = CHAIN_TO_SRV(ch); 963 no = ipfw_objhash_lookup_kidx(ni, idx); 964 KASSERT(no != NULL, ("NPT with index %d not found", idx)); 965 966 NPTV6_DEBUG("kidx %u -> %s", idx, no->name); 967 return (no); 968 } 969 970 static int 971 nptv6_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 972 enum ipfw_sets_cmd cmd) 973 { 974 975 return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NPTV6_NAME, 976 set, new_set, cmd)); 977 } 978 979 static struct opcode_obj_rewrite opcodes[] = { 980 { 981 .opcode = O_EXTERNAL_INSTANCE, 982 .etlv = IPFW_TLV_EACTION /* just show it isn't table */, 983 .classifier = nptv6_classify, 984 .update = nptv6_update_arg1, 985 .find_byname = nptv6_findbyname, 986 .find_bykidx = nptv6_findbykidx, 987 .manage_sets = nptv6_manage_sets, 988 }, 989 }; 990 991 static int 992 destroy_config_cb(struct namedobj_instance *ni, struct named_object *no, 993 void *arg) 994 { 995 struct nptv6_cfg *cfg; 996 struct ip_fw_chain *ch; 997 998 ch = (struct ip_fw_chain *)arg; 999 IPFW_UH_WLOCK_ASSERT(ch); 1000 1001 cfg = (struct nptv6_cfg *)SRV_OBJECT(ch, no->kidx); 1002 SRV_OBJECT(ch, no->kidx) = NULL; 1003 ipfw_objhash_del(ni, &cfg->no); 1004 ipfw_objhash_free_idx(ni, cfg->no.kidx); 1005 nptv6_free_config(cfg); 1006 return (0); 1007 } 1008 1009 int 1010 nptv6_init(struct ip_fw_chain *ch, int first) 1011 { 1012 1013 V_nptv6_eid = ipfw_add_eaction(ch, ipfw_nptv6, "nptv6"); 1014 if (V_nptv6_eid == 0) 1015 return (ENXIO); 1016 IPFW_ADD_SOPT_HANDLER(first, scodes); 1017 IPFW_ADD_OBJ_REWRITER(first, opcodes); 1018 return (0); 1019 } 1020 1021 void 1022 nptv6_uninit(struct ip_fw_chain *ch, int last) 1023 { 1024 1025 if (last && nptv6_ifaddr_event != NULL) 1026 EVENTHANDLER_DEREGISTER(ifaddr_event_ext, nptv6_ifaddr_event); 1027 IPFW_DEL_OBJ_REWRITER(last, opcodes); 1028 IPFW_DEL_SOPT_HANDLER(last, scodes); 1029 ipfw_del_eaction(ch, V_nptv6_eid); 1030 /* 1031 * Since we already have deregistered external action, 1032 * our named objects become unaccessible via rules, because 1033 * all rules were truncated by ipfw_del_eaction(). 1034 * So, we can unlink and destroy our named objects without holding 1035 * IPFW_WLOCK(). 1036 */ 1037 IPFW_UH_WLOCK(ch); 1038 ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch, 1039 IPFW_TLV_NPTV6_NAME); 1040 V_nptv6_eid = 0; 1041 IPFW_UH_WUNLOCK(ch); 1042 } 1043