ipv6.c revision 187770
11556Srgrimes/* 21556Srgrimes * Copyright (c) 2002-2003 Luigi Rizzo 31556Srgrimes * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 41556Srgrimes * Copyright (c) 1994 Ugen J.S.Antsilevich 51556Srgrimes * 61556Srgrimes * Idea and grammar partially left from: 71556Srgrimes * Copyright (c) 1993 Daniel Boulet 81556Srgrimes * 91556Srgrimes * Redistribution and use in source forms, with and without modification, 101556Srgrimes * are permitted provided that this entire comment appears intact. 111556Srgrimes * 121556Srgrimes * Redistribution in binary form may occur without any restrictions. 131556Srgrimes * Obviously, it would be nice if you gave credit where credit is due 141556Srgrimes * but requiring it would be too onerous. 151556Srgrimes * 161556Srgrimes * This software is provided ``AS IS'' without any warranties of any kind. 171556Srgrimes * 181556Srgrimes * NEW command line interface for IP firewall facility 191556Srgrimes * 201556Srgrimes * $FreeBSD: head/sbin/ipfw/ipv6.c 187770 2009-01-27 12:01:30Z luigi $ 211556Srgrimes * 221556Srgrimes * ipv6 support 231556Srgrimes */ 241556Srgrimes 251556Srgrimes#include <sys/types.h> 261556Srgrimes#include <sys/socket.h> 271556Srgrimes 281556Srgrimes#include "ipfw2.h" 291556Srgrimes 301556Srgrimes#include <err.h> 311556Srgrimes#include <netdb.h> 321556Srgrimes#include <stdio.h> 331556Srgrimes#include <stdlib.h> 341556Srgrimes#include <string.h> 351556Srgrimes#include <sysexits.h> 361556Srgrimes 371556Srgrimes#include <net/if.h> 3836150Scharnier#include <netinet/in.h> 3936150Scharnier#include <netinet/in_systm.h> 4036150Scharnier#include <netinet/ip.h> 411556Srgrimes#include <netinet/icmp6.h> 4299110Sobrien#include <netinet/ip_fw.h> 4399110Sobrien#include <arpa/inet.h> 441556Srgrimes 451556Srgrimesstatic struct _s_x icmp6codes[] = { 4646684Skris { "no-route", ICMP6_DST_UNREACH_NOROUTE }, 471556Srgrimes { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, 481556Srgrimes { "address", ICMP6_DST_UNREACH_ADDR }, 4917987Speter { "port", ICMP6_DST_UNREACH_NOPORT }, 5017987Speter { NULL, 0 } 5117987Speter}; 5217987Speter 5317987Spetervoid 5417987Speterfill_unreach6_code(u_short *codep, char *str) 5518016Speter{ 56104282Smux int val; 5718018Speter char *s; 5838536Scracauer 5929983Smsmith val = strtoul(str, &s, 0); 6017987Speter if (s == str || *s != '\0' || val >= 0x100) 611556Srgrimes val = match_token(icmp6codes, str); 621556Srgrimes if (val < 0) 631556Srgrimes errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); 641556Srgrimes *codep = val; 651556Srgrimes return; 661556Srgrimes} 671556Srgrimes 681556Srgrimesvoid 691556Srgrimesprint_unreach6_code(uint16_t code) 701556Srgrimes{ 711556Srgrimes char const *s = match_value(icmp6codes, code); 7250394Stg 7350394Stg if (s != NULL) 741556Srgrimes printf("unreach6 %s", s); 751556Srgrimes else 761556Srgrimes printf("unreach6 %u", code); 771556Srgrimes} 7817987Speter 7990111Simp/* 8017987Speter * Print the ip address contained in a command. 811556Srgrimes */ 821556Srgrimesvoid 831556Srgrimesprint_ip6(ipfw_insn_ip6 *cmd, char const *s) 8450394Stg{ 851556Srgrimes struct hostent *he = NULL; 861556Srgrimes int len = F_LEN((ipfw_insn *) cmd) - 1; 871556Srgrimes struct in6_addr *a = &(cmd->addr6); 881556Srgrimes char trad[255]; 891556Srgrimes 901556Srgrimes printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 9129983Smsmith 9229983Smsmith if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) { 9329983Smsmith printf("me6"); 9429983Smsmith return; 9529983Smsmith } 961556Srgrimes if (cmd->o.opcode == O_IP6) { 9750394Stg printf(" ip6"); 981556Srgrimes return; 9929983Smsmith } 10029983Smsmith 10150394Stg /* 10229983Smsmith * len == 4 indicates a single IP, whereas lists of 1 or more 10329983Smsmith * addr/mask pairs have len = (2n+1). We convert len to n so we 10459436Scracauer * use that to count the number of entries. 10529983Smsmith */ 10629983Smsmith 10729983Smsmith for (len = len / 4; len > 0; len -= 2, a += 2) { 10850394Stg int mb = /* mask length */ 10950394Stg (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ? 11050394Stg 128 : contigmask((uint8_t *)&(a[1]), 128); 11129983Smsmith 11259436Scracauer if (mb == 128 && co.do_resolv) 11359436Scracauer he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6); 11429983Smsmith if (he != NULL) /* resolved to name */ 11529983Smsmith printf("%s", he->h_name); 11629983Smsmith else if (mb == 0) /* any */ 11729983Smsmith printf("any"); 11829983Smsmith else { /* numeric IP followed by some kind of mask */ 11929983Smsmith if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL) 12029983Smsmith printf("Error ntop in print_ip6\n"); 12129983Smsmith printf("%s", trad ); 12229983Smsmith if (mb < 0) /* XXX not really legal... */ 12329983Smsmith printf(":%s", 12429983Smsmith inet_ntop(AF_INET6, &a[1], trad, sizeof(trad))); 12529983Smsmith else if (mb < 128) 12629983Smsmith printf("/%d", mb); 12729983Smsmith } 12829983Smsmith if (len > 2) 12929983Smsmith printf(","); 1301556Srgrimes } 1311556Srgrimes} 1321556Srgrimes 1331556Srgrimesvoid 1341556Srgrimesfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av) 1351556Srgrimes{ 1361556Srgrimes uint8_t type; 1371556Srgrimes 1381556Srgrimes bzero(cmd, sizeof(*cmd)); 13929983Smsmith while (*av) { 14029983Smsmith if (*av == ',') 14129983Smsmith av++; 14229983Smsmith type = strtoul(av, &av, 0); 14329983Smsmith if (*av != ',' && *av != '\0') 14429983Smsmith errx(EX_DATAERR, "invalid ICMP6 type"); 14529983Smsmith /* 14629983Smsmith * XXX: shouldn't this be 0xFF? I can't see any reason why 14729983Smsmith * we shouldn't be able to filter all possiable values 14829983Smsmith * regardless of the ability of the rest of the kernel to do 14929983Smsmith * anything useful with them. 15029983Smsmith */ 15129983Smsmith if (type > ICMP6_MAXTYPE) 15229983Smsmith errx(EX_DATAERR, "ICMP6 type out of range"); 15329983Smsmith cmd->d[type / 32] |= ( 1 << (type % 32)); 15429983Smsmith } 15529983Smsmith cmd->o.opcode = O_ICMP6TYPE; 15629983Smsmith cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6); 15729983Smsmith} 15829983Smsmith 15929983Smsmith 16029983Smsmithvoid 16129983Smsmithprint_icmp6types(ipfw_insn_u32 *cmd) 16229983Smsmith{ 16329983Smsmith int i, j; 16429983Smsmith char sep= ' '; 16529983Smsmith 16629983Smsmith printf(" ip6 icmp6types"); 16729983Smsmith for (i = 0; i < 7; i++) 1681556Srgrimes for (j=0; j < 32; ++j) { 1691556Srgrimes if ( (cmd->d[i] & (1 << (j))) == 0) 1701556Srgrimes continue; 1711556Srgrimes printf("%c%d", sep, (i*32 + j)); 1721556Srgrimes sep = ','; 17380381Ssheldonh } 1741556Srgrimes} 1751556Srgrimes 1761556Srgrimesvoid 1771556Srgrimesprint_flow6id( ipfw_insn_u32 *cmd) 1781556Srgrimes{ 1791556Srgrimes uint16_t i, limit = cmd->o.arg1; 1801556Srgrimes char sep = ','; 1811556Srgrimes 1821556Srgrimes printf(" flow-id "); 1831556Srgrimes for( i=0; i < limit; ++i) { 1841556Srgrimes if (i == limit - 1) 18550394Stg sep = ' '; 1861556Srgrimes printf("%d%c", cmd->d[i], sep); 1871556Srgrimes } 1881556Srgrimes} 1891556Srgrimes 1901556Srgrimes/* structure and define for the extension header in ipv6 */ 1911556Srgrimesstatic struct _s_x ext6hdrcodes[] = { 1921556Srgrimes { "frag", EXT_FRAGMENT }, 1931556Srgrimes { "hopopt", EXT_HOPOPTS }, 1941556Srgrimes { "route", EXT_ROUTING }, 1951556Srgrimes { "dstopt", EXT_DSTOPTS }, 19680381Ssheldonh { "ah", EXT_AH }, 1971556Srgrimes { "esp", EXT_ESP }, 1981556Srgrimes { "rthdr0", EXT_RTHDR0 }, 1991556Srgrimes { "rthdr2", EXT_RTHDR2 }, 2001556Srgrimes { NULL, 0 } 2011556Srgrimes}; 2021556Srgrimes 2031556Srgrimes/* fills command for the extension header filtering */ 2041556Srgrimesint 2051556Srgrimesfill_ext6hdr( ipfw_insn *cmd, char *av) 2061556Srgrimes{ 2071556Srgrimes int tok; 2081556Srgrimes char *s = av; 2091556Srgrimes 2101556Srgrimes cmd->arg1 = 0; 2111556Srgrimes 2121556Srgrimes while(s) { 2131556Srgrimes av = strsep( &s, ",") ; 2141556Srgrimes tok = match_token(ext6hdrcodes, av); 2151556Srgrimes switch (tok) { 2161556Srgrimes case EXT_FRAGMENT: 2171556Srgrimes cmd->arg1 |= EXT_FRAGMENT; 2181556Srgrimes break; 2191556Srgrimes 22017987Speter case EXT_HOPOPTS: 22190111Simp cmd->arg1 |= EXT_HOPOPTS; 22217987Speter break; 22317987Speter 2241556Srgrimes case EXT_ROUTING: 2251556Srgrimes cmd->arg1 |= EXT_ROUTING; 22617987Speter break; 2271556Srgrimes 22817987Speter case EXT_DSTOPTS: 22917987Speter cmd->arg1 |= EXT_DSTOPTS; 2301556Srgrimes break; 23111571Sjoerg 23217987Speter case EXT_AH: 23317987Speter cmd->arg1 |= EXT_AH; 23417987Speter break; 23517987Speter 23611571Sjoerg case EXT_ESP: 23717987Speter cmd->arg1 |= EXT_ESP; 23817987Speter break; 23917987Speter 24011571Sjoerg case EXT_RTHDR0: 24117987Speter cmd->arg1 |= EXT_RTHDR0; 24217987Speter break; 24317987Speter 24417987Speter case EXT_RTHDR2: 24517987Speter cmd->arg1 |= EXT_RTHDR2; 24617987Speter break; 24717987Speter 24817987Speter default: 24911571Sjoerg errx( EX_DATAERR, "invalid option for ipv6 exten header" ); 25017987Speter break; 25117987Speter } 25217987Speter } 25317987Speter if (cmd->arg1 == 0 ) 25417987Speter return 0; 25517987Speter cmd->opcode = O_EXT_HDR; 25617987Speter cmd->len |= F_INSN_SIZE( ipfw_insn ); 25717987Speter return 1; 25811571Sjoerg} 25917987Speter 26017987Spetervoid 26117987Speterprint_ext6hdr( ipfw_insn *cmd ) 26217987Speter{ 26317987Speter char sep = ' '; 26417987Speter 26517987Speter printf(" extension header:"); 26617987Speter if (cmd->arg1 & EXT_FRAGMENT ) { 26711571Sjoerg printf("%cfragmentation", sep); 26817987Speter sep = ','; 26917987Speter } 27017987Speter if (cmd->arg1 & EXT_HOPOPTS ) { 27117987Speter printf("%chop options", sep); 27217987Speter sep = ','; 27317987Speter } 27417987Speter if (cmd->arg1 & EXT_ROUTING ) { 27517987Speter printf("%crouting options", sep); 27617987Speter sep = ','; 27717987Speter } 27817987Speter if (cmd->arg1 & EXT_RTHDR0 ) { 27917987Speter printf("%crthdr0", sep); 28017987Speter sep = ','; 28117987Speter } 28220425Ssteve if (cmd->arg1 & EXT_RTHDR2 ) { 28317987Speter printf("%crthdr2", sep); 28441844Simp sep = ','; 28511571Sjoerg } 28617987Speter if (cmd->arg1 & EXT_DSTOPTS ) { 28717987Speter printf("%cdestination options", sep); 28841844Simp sep = ','; 28917987Speter } 29017987Speter if (cmd->arg1 & EXT_AH ) { 29111571Sjoerg printf("%cauthentication header", sep); 29211571Sjoerg sep = ','; 29311571Sjoerg } 29417987Speter if (cmd->arg1 & EXT_ESP ) { 29517987Speter printf("%cencapsulated security payload", sep); 29617987Speter } 29717987Speter} 29817987Speter 29917987Speter/* Try to find ipv6 address by hostname */ 30017987Speterstatic int 30117987Speterlookup_host6 (char *host, struct in6_addr *ip6addr) 30217987Speter{ 30311571Sjoerg struct hostent *he; 30417987Speter 30517987Speter if (!inet_pton(AF_INET6, host, ip6addr)) { 30618016Speter if ((he = gethostbyname2(host, AF_INET6)) == NULL) 30717987Speter return(-1); 30817987Speter memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr)); 30917987Speter } 31017987Speter return(0); 31111571Sjoerg} 31217987Speter 31317987Speter 31418016Speter/* 31517987Speter * fill the addr and mask fields in the instruction as appropriate from av. 31617987Speter * Update length as appropriate. 31718016Speter * The following formats are allowed: 31817987Speter * any matches any IP6. Actually returns an empty instruction. 31917987Speter * me returns O_IP6_*_ME 32018016Speter * 32117987Speter * 03f1::234:123:0342 single IP6 addres 32217987Speter * 03f1::234:123:0342/24 address/mask 32318016Speter * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address 32417987Speter * 32517987Speter * Set of address (as in ipv6) not supported because ipv6 address 32618016Speter * are typically random past the initial prefix. 32717987Speter * Return 1 on success, 0 on failure. 32817987Speter */ 32918016Speterstatic int 33017987Speterfill_ip6(ipfw_insn_ip6 *cmd, char *av) 33117987Speter{ 33218016Speter int len = 0; 33317987Speter struct in6_addr *d = &(cmd->addr6); 33417987Speter /* 33518016Speter * Needed for multiple address. 33617987Speter * Note d[1] points to struct in6_add r mask6 of cmd 33717987Speter */ 33818016Speter 33917987Speter cmd->o.len &= ~F_LEN_MASK; /* zero len */ 34017987Speter 34118016Speter if (strcmp(av, "any") == 0) 34217987Speter return (1); 34317987Speter 34418016Speter 34517987Speter if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/ 34652072Sgreen cmd->o.len |= F_INSN_SIZE(ipfw_insn); 34752072Sgreen return (1); 34852072Sgreen } 34918016Speter 35017987Speter if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/ 35117987Speter cmd->o.len |= F_INSN_SIZE(ipfw_insn); 35217987Speter return (1); 35390111Simp } 35417987Speter 35525222Ssteve av = strdup(av); 356104282Smux while (av) { 35717987Speter /* 35817987Speter * After the address we can have '/' indicating a mask, 35917987Speter * or ',' indicating another address follows. 36017987Speter */ 36117987Speter 36217987Speter char *p; 36311571Sjoerg int masklen; 36417987Speter char md = '\0'; 36598834Sdillon 36617987Speter if ((p = strpbrk(av, "/,")) ) { 36711571Sjoerg md = *p; /* save the separator */ 36817987Speter *p = '\0'; /* terminate address string */ 36911571Sjoerg p++; /* and skip past it */ 37011571Sjoerg } 37117987Speter /* now p points to NULL, mask or next entry */ 37211571Sjoerg 37311571Sjoerg /* lookup stores address in *d as a side effect */ 37417987Speter if (lookup_host6(av, d) != 0) { 37511571Sjoerg /* XXX: failed. Free memory and go */ 37617987Speter errx(EX_DATAERR, "bad address \"%s\"", av); 37717987Speter } 37811571Sjoerg /* next, look at the mask, if any */ 37911571Sjoerg masklen = (md == '/') ? atoi(p) : 128; 38017987Speter if (masklen > 128 || masklen < 0) 38117987Speter errx(EX_DATAERR, "bad width \"%s\''", p); 38217987Speter else 383104208Stjr n2mask(&d[1], masklen); 38417987Speter 38517987Speter APPLY_MASK(d, &d[1]) /* mask base address with mask */ 38617987Speter 38717987Speter /* find next separator */ 38817987Speter 38917987Speter if (md == '/') { /* find separator past the mask */ 390104208Stjr p = strpbrk(p, ","); 39117987Speter if (p != NULL) 39211571Sjoerg p++; 39311571Sjoerg } 394104282Smux av = p; 39517987Speter 39617987Speter /* Check this entry */ 39717987Speter if (masklen == 0) { 39817987Speter /* 399104282Smux * 'any' turns the entire list into a NOP. 40017987Speter * 'not any' never matches, so it is removed from the 40117987Speter * list unless it is the only item, in which case we 40217987Speter * report an error. 403104208Stjr */ 40417987Speter if (cmd->o.len & F_NOT && av == NULL && len == 0) 40511571Sjoerg errx(EX_DATAERR, "not any never matches"); 40617987Speter continue; 40717987Speter } 40818016Speter 40918016Speter /* 41018016Speter * A single IP can be stored alone 411104208Stjr */ 41217987Speter if (masklen == 128 && av == NULL && len == 0) { 41317987Speter len = F_INSN_SIZE(struct in6_addr); 41417987Speter break; 41517987Speter } 41617987Speter 41718016Speter /* Update length and pointer to arguments */ 41818016Speter len += F_INSN_SIZE(struct in6_addr)*2; 41918019Speter d += 2; 42018016Speter } /* end while */ 42118016Speter 42218019Speter /* 42318019Speter * Total length of the command, remember that 1 is the size of 42417987Speter * the base command. 42517987Speter */ 42617987Speter if (len + 1 > F_LEN_MASK) 42717987Speter errx(EX_DATAERR, "address list too long"); 42817987Speter cmd->o.len |= len+1; 429104282Smux free(av); 43017987Speter return (1); 43111571Sjoerg} 43217987Speter 43311571Sjoerg/* 43417987Speter * fills command for ipv6 flow-id filtering 43518016Speter * note that the 20 bit flow number is stored in a array of u_int32_t 436104208Stjr * it's supported lists of flow-id, so in the o.arg1 we store how many 43717987Speter * additional flow-id we want to filter, the basic is 1 43817987Speter */ 43917987Spetervoid 44017987Speterfill_flow6( ipfw_insn_u32 *cmd, char *av ) 44117987Speter{ 44217987Speter u_int32_t type; /* Current flow number */ 443104208Stjr u_int16_t nflow = 0; /* Current flow index */ 44417987Speter char *s = av; 44517987Speter cmd->d[0] = 0; /* Initializing the base number*/ 44617987Speter 44717987Speter while (s) { 44817987Speter av = strsep( &s, ",") ; 44917987Speter type = strtoul(av, &av, 0); 45017987Speter if (*av != ',' && *av != '\0') 45117987Speter errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 45217987Speter if (type > 0xfffff) 45317987Speter errx(EX_DATAERR, "flow number out of range %s", av); 45417987Speter cmd->d[nflow] |= type; 455104282Smux nflow++; 45617987Speter } 45717987Speter if( nflow > 0 ) { 45811571Sjoerg cmd->o.opcode = O_FLOW6ID; 45911571Sjoerg cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow; 460 cmd->o.arg1 = nflow; 461 } 462 else { 463 errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 464 } 465} 466 467ipfw_insn * 468add_srcip6(ipfw_insn *cmd, char *av) 469{ 470 471 fill_ip6((ipfw_insn_ip6 *)cmd, av); 472 if (F_LEN(cmd) == 0) { /* any */ 473 } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 474 cmd->opcode = O_IP6_SRC_ME; 475 } else if (F_LEN(cmd) == 476 (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 477 /* single IP, no mask*/ 478 cmd->opcode = O_IP6_SRC; 479 } else { /* addr/mask opt */ 480 cmd->opcode = O_IP6_SRC_MASK; 481 } 482 return cmd; 483} 484 485ipfw_insn * 486add_dstip6(ipfw_insn *cmd, char *av) 487{ 488 489 fill_ip6((ipfw_insn_ip6 *)cmd, av); 490 if (F_LEN(cmd) == 0) { /* any */ 491 } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 492 cmd->opcode = O_IP6_DST_ME; 493 } else if (F_LEN(cmd) == 494 (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 495 /* single IP, no mask*/ 496 cmd->opcode = O_IP6_DST; 497 } else { /* addr/mask opt */ 498 cmd->opcode = O_IP6_DST_MASK; 499 } 500 return cmd; 501} 502