*** diffs for ipfw6 changes *** *** Cleanup diffs *** --- ../freebsd/sbin/ipfw/ipfw2.c Mon Nov 29 11:36:53 2004 +++ ../cleanup/sbin/ipfw/ipfw2.c Mon Nov 29 11:45:53 2004 @@ -2522,17 +2522,17 @@ a = (a == 32) ? ~0 : (1 << a) - 1; } else a = strtoul(av[0], &end, 0); - if (p32 != NULL) + if (p32 != NULL) { *p32 = a; - else if (p16 != NULL) { - if (a > 65535) + } else if (p16 != NULL) { + if (a > 0xffff) errx(EX_DATAERR, - "mask: must be 16 bit"); + "port mask must be 16 bit"); *p16 = (uint16_t)a; } else { - if (a > 255) + if (a > 0xff) errx(EX_DATAERR, - "mask: must be 8 bit"); + "proto mask must be 8 bit"); p.fs.flow_mask.proto = (uint8_t)a; } if (a != 0) --- ../freebsd/sys/netinet/ip_dummynet.c Thu Aug 26 20:59:36 2004 +++ ../cleanup/sys/netinet/ip_dummynet.c Mon Nov 22 12:52:14 2004 @@ -1065,7 +1065,7 @@ { #if IPFW2 struct dn_flow_set *fs; - ipfw_insn *cmd = rule->cmd + rule->act_ofs; + ipfw_insn *cmd = ACTION_PTR(rule); if (cmd->opcode == O_LOG) cmd += F_LEN(cmd); @@ -1132,7 +1132,7 @@ struct dn_flow_queue *q = NULL ; int is_pipe; #if IPFW2 - ipfw_insn *cmd = fwa->rule->cmd + fwa->rule->act_ofs; + ipfw_insn *cmd = ACTION_PTR(fwa->rule); #endif KASSERT(m->m_nextpkt == NULL, @@ -1372,7 +1372,7 @@ * remove references from all ipfw rules to all pipes. */ static void -dummynet_flush() +dummynet_flush(void) { struct dn_pipe *curr_p, *p ; struct dn_flow_set *fs, *curr_fs; --- ../freebsd/sys/netinet/ip_fw2.c Mon Dec 13 12:52:04 2004 +++ ../cleanup/sys/netinet/ip_fw2.c Mon Dec 13 22:07:04 2004 @@ -314,14 +314,18 @@ /* - * This macro maps an ip pointer into a layer3 header pointer of type T + * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T + * Other macros just cast void * into the appropriate type */ -#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) +#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) +#define TCP(p) ((struct tcphdr *)(p)) +#define UDP(p) ((struct udphdr *)(p)) +#define ICMP(p) ((struct icmp *)(p)) static __inline int -icmptype_match(struct ip *ip, ipfw_insn_u32 *cmd) +icmptype_match(struct icmp *icmp, ipfw_insn_u32 *cmd) { - int type = L3HDR(struct icmp,ip)->icmp_type; + int type = icmp->icmp_type; return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<icmp_type; + int type = icmp->icmp_type; + return (type <= ICMP_MAXTYPE && (TT & (1<th_off << 2) - sizeof(struct tcphdr); @@ -1800,97 +1804,112 @@ struct in_addr src_ip, dst_ip; /* NOTE: network format */ u_int16_t ip_len=0; int pktlen; - int dyn_dir = MATCH_UNKNOWN; - ipfw_dyn_rule *q = NULL; - struct ip_fw_chain *chain = &layer3_chain; - struct m_tag *mtag; - if (m->m_flags & M_SKIP_FIREWALL) - return 0; /* accept */ /* * dyn_dir = MATCH_UNKNOWN when rules unchecked, * MATCH_NONE when checked and not matched (q = NULL), * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ - - pktlen = m->m_pkthdr.len; - if (args->eh == NULL || /* layer 3 packet */ - ( m->m_pkthdr.len >= sizeof(struct ip) && - ntohs(args->eh->ether_type) == ETHERTYPE_IP)) - hlen = ip->ip_hl << 2; + int dyn_dir = MATCH_UNKNOWN; + ipfw_dyn_rule *q = NULL; + struct ip_fw_chain *chain = &layer3_chain; + struct m_tag *mtag; /* - * Collect parameters into local variables for faster matching. + * We store in ulp a pointer to the upper layer protocol header. + * In the ipv4 case this is easy to determine from the header, + * but for ipv6 we might have some additional headers in the middle. + * ulp is NULL if not found. */ - if (hlen == 0) { /* do not grab addresses for non-ip pkts */ - proto = args->f_id.proto = 0; /* mark f_id invalid */ - goto after_ip_checks; - } + void *ulp = NULL; /* upper layer protocol pointer. */ - proto = args->f_id.proto = ip->ip_p; - src_ip = ip->ip_src; - dst_ip = ip->ip_dst; - if (args->eh != NULL) { /* layer 2 packets are as on the wire */ - offset = ntohs(ip->ip_off) & IP_OFFMASK; - ip_len = ntohs(ip->ip_len); - } else { - offset = ip->ip_off & IP_OFFMASK; - ip_len = ip->ip_len; - } - pktlen = ip_len < pktlen ? ip_len : pktlen; + if (m->m_flags & M_SKIP_FIREWALL) + return 0; /* accept */ -#define PULLUP_TO(len) \ - do { \ - if ((m)->m_len < (len)) { \ - args->m = m = m_pullup(m, (len)); \ - if (m == 0) \ - goto pullup_failed; \ - ip = mtod(m, struct ip *); \ - } \ - } while (0) + pktlen = m->m_pkthdr.len; + proto = args->f_id.proto = 0; /* mark f_id invalid */ - if (offset == 0) { - switch (proto) { - case IPPROTO_TCP: - { - struct tcphdr *tcp; +/* + * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, + * then it sets p to point at the offset "len" in the mbuf. WARNING: the + * pointer might become stale after other pullups (but we never use it + * this way). + */ +#define PULLUP_TO(len, p, T) \ +do { \ + int x = (len) + sizeof(T); \ + if ((m)->m_len < x) { \ + args->m = m = m_pullup(m, x); \ + if (m == NULL) \ + goto pullup_failed; \ + } \ + p = (mtod(m, char *) + (len)); \ +} while (0) + + /* Identify IP packets and fill up veriables. */ + if (pktlen >= sizeof(struct ip) && + (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && + mtod(m, struct ip *)->ip_v == 4) { + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; +#ifdef NOTYET + args->f_id.addr_type = 4; +#endif - PULLUP_TO(hlen + sizeof(struct tcphdr)); - tcp = L3HDR(struct tcphdr, ip); - dst_port = tcp->th_dport; - src_port = tcp->th_sport; - args->f_id.flags = tcp->th_flags; - } - break; + /* + * Collect parameters into local variables for faster matching. + */ + proto = ip->ip_p; + src_ip = ip->ip_src; + dst_ip = ip->ip_dst; + if (args->eh != NULL) { /* layer 2 packets are as on the wire */ + offset = ntohs(ip->ip_off) & IP_OFFMASK; + ip_len = ntohs(ip->ip_len); + } else { + offset = ip->ip_off & IP_OFFMASK; + ip_len = ip->ip_len; + } + pktlen = ip_len < pktlen ? ip_len : pktlen; - case IPPROTO_UDP: - { - struct udphdr *udp; + if (offset == 0) { + switch (proto) { + case IPPROTO_TCP: + PULLUP_TO(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + args->f_id.flags = TCP(ulp)->th_flags; + break; - PULLUP_TO(hlen + sizeof(struct udphdr)); - udp = L3HDR(struct udphdr, ip); - dst_port = udp->uh_dport; - src_port = udp->uh_sport; - } - break; + case IPPROTO_UDP: + PULLUP_TO(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; - case IPPROTO_ICMP: - PULLUP_TO(hlen + 4); /* type, code and checksum. */ - args->f_id.flags = L3HDR(struct icmp, ip)->icmp_type; - break; + case IPPROTO_ICMP: + /* + * we only care for 4 bytes: type, code, + * checksum + */ + PULLUP_TO(hlen, ulp, struct icmp); + args->f_id.flags = ICMP(ulp)->icmp_type; + break; - default: - break; + default: + break; + } } + + args->f_id.src_ip = ntohl(src_ip.s_addr); + args->f_id.dst_ip = ntohl(dst_ip.s_addr); + } #undef PULLUP_TO + if (proto) { /* we may have port numbers, store them */ + args->f_id.proto = proto; + args->f_id.src_port = src_port = ntohs(src_port); + args->f_id.dst_port = dst_port = ntohs(dst_port); } - args->f_id.src_ip = ntohl(src_ip.s_addr); - args->f_id.dst_ip = ntohl(dst_ip.s_addr); - args->f_id.src_port = src_port = ntohs(src_port); - args->f_id.dst_port = dst_port = ntohs(dst_port); - -after_ip_checks: IPFW_RLOCK(chain); mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); if (args->rule) { @@ -2187,7 +2206,7 @@ case O_ICMPTYPE: match = (offset == 0 && proto==IPPROTO_ICMP && - icmptype_match(ip, (ipfw_insn_u32 *)cmd) ); + icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); break; case O_IPOPT: @@ -2241,7 +2260,7 @@ uint16_t *p; int i; - tcp = L3HDR(struct tcphdr,ip); + tcp = TCP(ulp); x = ip_len - ((ip->ip_hl + tcp->th_off) << 2); if (cmdlen == 1) { @@ -2258,38 +2277,36 @@ case O_TCPFLAGS: match = (proto == IPPROTO_TCP && offset == 0 && - flags_match(cmd, - L3HDR(struct tcphdr,ip)->th_flags)); + flags_match(cmd, TCP(ulp)->th_flags)); break; case O_TCPOPTS: match = (proto == IPPROTO_TCP && offset == 0 && - tcpopts_match(ip, cmd)); + tcpopts_match(TCP(ulp), cmd)); break; case O_TCPSEQ: match = (proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == - L3HDR(struct tcphdr,ip)->th_seq); + TCP(ulp)->th_seq); break; case O_TCPACK: match = (proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == - L3HDR(struct tcphdr,ip)->th_ack); + TCP(ulp)->th_ack); break; case O_TCPWIN: match = (proto == IPPROTO_TCP && offset == 0 && - cmd->arg1 == - L3HDR(struct tcphdr,ip)->th_win); + cmd->arg1 == TCP(ulp)->th_win); break; case O_ESTAB: /* reject packets which have SYN only */ /* XXX should i also check for TH_ACK ? */ match = (proto == IPPROTO_TCP && offset == 0 && - (L3HDR(struct tcphdr,ip)->th_flags & + (TCP(ulp)->th_flags & (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); break; @@ -2428,7 +2445,7 @@ if (dyn_dir == MATCH_UNKNOWN && (q = lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? - L3HDR(struct tcphdr, ip) : NULL)) + TCP(ulp) : NULL)) != NULL) { /* * Found dynamic entry, update stats @@ -2509,7 +2526,7 @@ */ if (hlen > 0 && (proto != IPPROTO_ICMP || - is_icmp_query(ip)) && + is_icmp_query(ICMP(ulp))) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN_MULTICAST(ntohl(dst_ip.s_addr))) { send_reject(args, cmd->arg1, --- ../freebsd/sys/netinet6/ip6_output.c Thu Oct 7 11:18:35 2004 +++ ../cleanup/sys/netinet6/ip6_output.c Wed Oct 6 18:30:54 2004 @@ -220,6 +220,7 @@ /* Destination options header(2nd part) */ MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); } +#undef MAKE_EXTHDR #ifdef IPSEC /* get a security policy for this packet */ @@ -389,18 +390,18 @@ ip6->ip6_nxt = IPPROTO_DSTOPTS; } -#define MAKE_CHAIN(m, mp, p, i)\ - do {\ - if (m) {\ - if (!hdrsplit) \ - panic("assumption failed: hdr not split"); \ - *mtod((m), u_char *) = *(p);\ - *(p) = (i);\ - p = mtod((m), u_char *);\ - (m)->m_next = (mp)->m_next;\ - (mp)->m_next = (m);\ - (mp) = (m);\ - }\ +#define MAKE_CHAIN(m, mp, p, i) \ + do { \ + if (m) { \ + if (!hdrsplit) \ + panic("assumption failed: hdr not split"); \ + *mtod((m), u_char *) = *(p); \ + *(p) = (i); \ + p = mtod((m), u_char *); \ + (m)->m_next = (mp)->m_next; \ + (mp)->m_next = (m); \ + (mp) = (m); \ + } \ } while (/*CONSTCOND*/ 0) /* * result: IPv6 hbh dest1 rthdr dest2 payload @@ -412,6 +413,7 @@ IPPROTO_DSTOPTS); MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, IPPROTO_ROUTING); +#undef MAKE_CHAIN #if defined(IPSEC) || defined(FAST_IPSEC) if (!needipsec) @@ -465,7 +467,7 @@ rh->ip6r_segleft = segleft_org; } } -skip_ipsec2:; +skip_ipsec2: #endif } @@ -475,8 +477,7 @@ */ if (exthdrs.ip6e_rthdr) { struct ip6_rthdr *rh = - (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, - struct ip6_rthdr *)); + mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *); struct ip6_rthdr0 *rh0; struct in6_addr *addrs; @@ -535,7 +536,7 @@ RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } - if (ro->ro_rt == 0) { + if (ro->ro_rt == NULL) { bzero(dst, sizeof(*dst)); dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(struct sockaddr_in6); Changed files: sbin/ipfw/ipfw2.c sys/netinet/ip_dummynet.c sys/netinet/ip_fw2.c sys/netinet6/ip6_output.c *** Functional diffs *** --- ../cleanup/sbin/ipfw/ipfw2.c Mon Nov 29 11:45:53 2004 +++ sbin/ipfw/ipfw2.c Mon Dec 20 13:16:12 2004 @@ -49,10 +49,12 @@ #include #include +#include /* def. of struct route */ #include #include #include #include +#include #include #include #include @@ -264,6 +266,13 @@ TOK_DROPTAIL, TOK_PROTO, TOK_WEIGHT, + + TOK_IPV6, + TOK_FLOWID, + TOK_ICMP6TYPES, + TOK_EXT6HDR, + TOK_DSTIP6, + TOK_SRCIP6, }; struct _s_x dummynet_params[] = { @@ -286,6 +295,11 @@ { "delay", TOK_DELAY }, { "pipe", TOK_PIPE }, { "queue", TOK_QUEUE }, + { "flow-id", TOK_FLOWID}, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, { "dummynet-params", TOK_NULL }, { NULL, 0 } /* terminator */ }; @@ -371,6 +385,16 @@ { "versrcreach", TOK_VERSRCREACH }, { "antispoof", TOK_ANTISPOOF }, { "ipsec", TOK_IPSEC }, + { "icmp6type", TOK_ICMP6TYPES }, + { "icmp6types", TOK_ICMP6TYPES }, + { "ext6hdr", TOK_EXT6HDR}, + { "flow-id", TOK_FLOWID}, + { "ipv6", TOK_IPV6}, + { "ip6", TOK_IPV6}, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ @@ -971,6 +995,199 @@ } } +/* + * Print the ip address contained in a command. + */ +static void +print_ip6(ipfw_insn_ip6 *cmd, char const *s) +{ + struct hostent *he = NULL; + int len = F_LEN((ipfw_insn *) cmd) - 1; + struct in6_addr *a = &(cmd->addr6); + char trad[255]; + + printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); + + if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) { + printf("me6"); + return; + } + if (cmd->o.opcode == O_IP6) { + printf(" ipv6"); + return; + } + + /* + * len == 4 indicates a single IP, whereas lists of 1 or more + * addr/mask pairs have len = (2n+1). We convert len to n so we + * use that to count the number of entries. + */ + + for (len = len / 4; len > 0; len -= 2, a += 2) { + int mb = /* mask length */ + (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ? + 128 : contigmask((uint8_t *)&(a[1]), 128); + + if (mb == 128 && do_resolv) + he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6); + if (he != NULL) /* resolved to name */ + printf("%s", he->h_name); + else if (mb == 0) /* any */ + printf("any"); + else { /* numeric IP followed by some kind of mask */ + if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL) + printf("Error ntop in print_ip6\n"); + printf("%s", trad ); + if (mb < 0) /* XXX not really legal... */ + printf(":%s", + inet_ntop(AF_INET6, &a[1], trad, sizeof(trad))); + else if (mb < 128) + printf("/%d", mb); + } + if (len > 2) + printf(","); + } +} + +static void +fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av) +{ + uint8_t type; + + cmd->d[0] = 0; + while (*av) { + if (*av == ',') + av++; + type = strtoul(av, &av, 0); + if (*av != ',' && *av != '\0') + errx(EX_DATAERR, "invalid ICMP6 type"); + /* + * XXX: shouldn't this be 0xFF? I can't see any reason why + * we shouldn't be able to filter all possiable values + * regardless of the ability of the rest of the kernel to do + * anything useful with them. + */ + if (type > ICMP6_MAXTYPE) + errx(EX_DATAERR, "ICMP6 type out of range"); + cmd->d[type / 32] |= ( 1 << (type % 32)); + } + cmd->o.opcode = O_ICMP6TYPE; + cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6); +} + + +static void +print_icmp6types(ipfw_insn_u32 *cmd) +{ + int i, j; + char sep= ' '; + + printf(" ipv6 icmp6types"); + for (i = 0; i < 7; i++) + for (j=0; j < 32; ++j) { + if ( (cmd->d[i] & (1 << (j))) == 0) + continue; + printf("%c%d", sep, (i*32 + j)); + sep = ','; + } +} + +static void +print_flow6id( ipfw_insn_u32 *cmd) +{ + uint16_t i, limit = cmd->o.arg1; + char sep = ','; + + printf(" flow-id "); + for( i=0; i < limit; ++i) { + if (i == limit - 1) + sep = ' '; + printf("%d%c", cmd->d[i], sep); + } +} + +/* structure and define for the extension header in ipv6 */ +static struct _s_x ext6hdrcodes[] = { + { "frag", EXT_FRAGMENT }, + { "hopopt", EXT_HOPOPTS }, + { "route", EXT_ROUTING }, + { "ah", EXT_AH }, + { "esp", EXT_ESP }, + { NULL, 0 } +}; + +/* fills command for the extension header filtering */ +int +fill_ext6hdr( ipfw_insn *cmd, char *av) +{ + int tok; + char *s = av; + + cmd->arg1 = 0; + + while(s) { + av = strsep( &s, ",") ; + tok = match_token(ext6hdrcodes, av); + switch (tok) { + case EXT_FRAGMENT: + cmd->arg1 |= EXT_FRAGMENT; + break; + + case EXT_HOPOPTS: + cmd->arg1 |= EXT_HOPOPTS; + break; + + case EXT_ROUTING: + cmd->arg1 |= EXT_ROUTING; + break; + + case EXT_AH: + cmd->arg1 |= EXT_AH; + break; + + case EXT_ESP: + cmd->arg1 |= EXT_ESP; + break; + + default: + errx( EX_DATAERR, "invalid option for ipv6 exten header" ); + break; + } + } + if (cmd->arg1 == 0 ) + return 0; + cmd->opcode = O_EXT_HDR; + cmd->len |= F_INSN_SIZE( ipfw_insn ); + return 1; +} + +void +print_ext6hdr( ipfw_insn *cmd ) +{ + char sep = ' '; + + printf(" extension header:"); + if (cmd->arg1 & EXT_FRAGMENT ) { + printf("%cfragmentation", sep); + sep = ','; + } + if (cmd->arg1 & EXT_HOPOPTS ) { + printf("%chop options", sep); + sep = ','; + } + if (cmd->arg1 & EXT_ROUTING ) { + printf("%crouting options", sep); + sep = ','; + } + if (cmd->arg1 & EXT_AH ) { + printf("%cauthentication header", sep); + sep = ','; + } + if (cmd->arg1 & EXT_ESP ) { + printf("%cencapsulated security payload", sep); + } +} + /* * show_ipfw() prints the body of an ipfw rule. * Because the standard rule has at least proto src_ip dst_ip, we use @@ -989,6 +1206,7 @@ #define HAVE_DSTIP 0x0004 #define HAVE_MAC 0x0008 #define HAVE_MACTYPE 0x0010 +#define HAVE_PROTO6 0x0080 #define HAVE_OPTIONS 0x8000 #define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) @@ -1011,6 +1229,9 @@ return; } if ( !(*flags & HAVE_OPTIONS)) { + /* XXX BED: !(*flags & HAVE_PROTO) in patch */ + if ( !(*flags & HAVE_PROTO6) && (want & HAVE_PROTO6)) + printf(" ipv6"); if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) printf(" ip"); if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) @@ -1267,6 +1488,37 @@ flags |= HAVE_DSTIP; break; + case O_IP6_SRC: + case O_IP6_SRC_MASK: + case O_IP6_SRC_ME: + show_prerequisites(&flags, HAVE_PROTO6, 0); + if (!(flags & HAVE_SRCIP)) + printf(" from"); + if ((cmd->len & F_OR) && !or_block) + printf(" {"); + print_ip6((ipfw_insn_ip6 *)cmd, + (flags & HAVE_OPTIONS) ? " src-ip6" : ""); + flags |= HAVE_SRCIP | HAVE_PROTO; + break; + + case O_IP6_DST: + case O_IP6_DST_MASK: + case O_IP6_DST_ME: + show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); + if (!(flags & HAVE_DSTIP)) + printf(" to"); + if ((cmd->len & F_OR) && !or_block) + printf(" {"); + print_ip6((ipfw_insn_ip6 *)cmd, + (flags & HAVE_OPTIONS) ? " dst-ip6" : ""); + flags |= HAVE_DSTIP; + break; + + case O_FLOW6ID: + print_flow6id( (ipfw_insn_u32 *) cmd ); + flags |= HAVE_OPTIONS; + break; + case O_IP_DSTPORT: show_prerequisites(&flags, HAVE_IP, 0); case O_IP_SRCPORT: @@ -1278,14 +1530,15 @@ break; case O_PROTO: { - struct protoent *pe; + struct protoent *pe = NULL; if ((cmd->len & F_OR) && !or_block) printf(" {"); if (cmd->len & F_NOT) printf(" not"); proto = cmd->arg1; - pe = getprotobynumber(cmd->arg1); + if (proto != 41) /* XXX: IPv6 is special */ + pe = getprotobynumber(cmd->arg1); if (flags & HAVE_OPTIONS) printf(" proto"); if (pe) @@ -1494,6 +1747,18 @@ } break; + case O_IP6: + printf(" ipv6"); + break; + + case O_ICMP6TYPE: + print_icmp6types((ipfw_insn_u32 *)cmd); + break; + + case O_EXT_HDR: + print_ext6hdr( (ipfw_insn *) cmd ); + break; + default: printf(" [opcode %d len %d]", cmd->opcode, cmd->len); @@ -1591,29 +1856,48 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) { int l; + int index_printed, indexes = 0; + char buff[255]; + struct protoent *pe; - printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", - fs->flow_mask.proto, - fs->flow_mask.src_ip, fs->flow_mask.src_port, - fs->flow_mask.dst_ip, fs->flow_mask.dst_port); if (fs->rq_elements == 0) return; - printf("BKT Prot ___Source IP/port____ " - "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); if (do_sort != 0) heapsort(q, fs->rq_elements, sizeof *q, sort_q); + + /* Print IPv4 flows */ + index_printed = 0; for (l = 0; l < fs->rq_elements; l++) { struct in_addr ina; - struct protoent *pe; - ina.s_addr = htonl(q[l].id.src_ip); + /* XXX: Should check for IPv4 flows */ + if (IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) /* currently a no-op */ + printf("\n"); + indexes++; + printf(" " + "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + + printf("BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf("%3d ", q[l].hash_slot); pe = getprotobynumber(q[l].id.proto); if (pe) printf("%-4s ", pe->p_name); else printf("%4u ", q[l].id.proto); + ina.s_addr = htonl(q[l].id.src_ip); printf("%15s/%-5d ", inet_ntoa(ina), q[l].id.src_port); ina.s_addr = htonl(q[l].id.dst_ip); @@ -1626,6 +1910,50 @@ printf(" S %20qd F %20qd\n", q[l].S, q[l].F); } + + /* Print IPv6 flows */ + index_printed = 0; + for (l = 0; l < fs->rq_elements; l++) { + if (!IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) + printf("\n"); + indexes++; + printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", + fs->flow_mask.proto, fs->flow_mask.flow_id6); + inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6), + buff, sizeof(buff)); + printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port); + inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6), + buff, sizeof(buff) ); + printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port); + + printf("BKT ___Prot___ _flow-id_ " + "______________Source IPv6/port_______________ " + "_______________Dest. IPv6/port_______________ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf("%3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe != NULL) + printf("%9s ", pe->p_name); + else + printf("%9u ", q[l].id.proto); + printf("%7d %39s/%-5d ", q[l].id.flow_id6, + inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)), + q[l].id.src_port); + printf(" %39s/%-5d ", + inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)), + q[l].id.dst_port); + printf(" %4qu %8qu %2u %4u %3u\n", + q[l].tot_pkts, q[l].tot_bytes, + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20qd F %20qd\n", q[l].S, q[l].F); + } } static void @@ -2018,7 +2346,7 @@ if (do_dynamic && ndyn) { printf("## Dynamic rules:\n"); for (lac = ac, lav = av; lac != 0; lac--) { - rnum = strtoul(*lav++, &endptr, 10); + last = rnum = strtoul(*lav++, &endptr, 10); if (*endptr == '-') last = strtoul(endptr+1, &endptr, 10); if (*endptr) @@ -2068,19 +2396,24 @@ "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" "\n" "RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" -"ACTION: check-state | allow | count | deny | reject | skipto N |\n" +"ACTION: check-state | allow | count | deny | unreach CODE | skipto N |\n" " {divert|tee} PORT | forward ADDR | pipe N | queue N\n" "PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" "ADDR: [ MAC dst src ether_type ] \n" -" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" "IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" +"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" +"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" "IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" "OPTION_LIST: OPTION [OPTION_LIST]\n" "OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" -" {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n" +" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" +" {dst-port|src-port} LIST |\n" " estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" " iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" " ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" +" icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n" " mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" " tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" @@ -2106,7 +2439,6 @@ * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. * The following formats are allowed: - * any matches any IP. Actually returns an empty instruction. * me returns O_IP_*_ME * 1.2.3.4 single IP address * 1.2.3.4:5.6.7.8 address:mask @@ -2293,6 +2625,231 @@ } +/* Try to find ipv6 address by hostname */ +static int +lookup_host6 (char *host, struct in6_addr *ip6addr) +{ + struct hostent *he; + + if (!inet_pton(AF_INET6, host, ip6addr)) { + if ((he = gethostbyname2(host, AF_INET6)) == NULL) + return(-1); + memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr)); + } + return(0); +} + + +/* n2mask sets n bits of the mask */ +static void +n2mask(struct in6_addr *mask, int n) +{ + static int minimask[9] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + u_char *p; + + memset(mask, 0, sizeof(struct in6_addr)); + p = (u_char *) mask; + for (; n > 0; p++, n -= 8) { + if (n >= 8) + *p = 0xff; + else + *p = minimask[n]; + } + return; +} + + +/* + * fill the addr and mask fields in the instruction as appropriate from av. + * Update length as appropriate. + * The following formats are allowed: + * any matches any IP6. Actually returns an empty instruction. + * me returns O_IP6_*_ME + * + * 03f1::234:123:0342 single IP6 addres + * 03f1::234:123:0342/24 address/mask + * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address + * + * Set of address (as in ipv6) not supported because ipv6 address + * are typically random past the initial prefix. + * Return 1 on success, 0 on failure. + */ +static int +fill_ip6(ipfw_insn_ip6 *cmd, char *av) +{ + int len = 0; + struct in6_addr *d = &(cmd->addr6); + /* + * Needed for multiple address. + * Note d[1] points to struct in6_add r mask6 of cmd + */ + + cmd->o.len &= ~F_LEN_MASK; /* zero len */ + + if (strcmp(av, "any") == 0) + return (1); + + + if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/ + cmd->o.len |= F_INSN_SIZE(ipfw_insn); + return (1); + } + + if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/ + cmd->o.len |= F_INSN_SIZE(ipfw_insn); + return (1); + } + + av = strdup(av); + while (av) { + /* + * After the address we can have '/' indicating a mask, + * or ',' indicating another address follows. + */ + + char *p; + int masklen; + char md = '\0'; + + if ((p = strpbrk(av, "/,")) ) { + md = *p; /* save the separator */ + *p = '\0'; /* terminate address string */ + p++; /* and skip past it */ + } + /* now p points to NULL, mask or next entry */ + + /* lookup stores address in *d as a side effect */ + if (lookup_host6(av, d) != 0) { + /* XXX: failed. Free memory and go */ + errx(EX_DATAERR, "bad address \"%s\"", av); + } + /* next, look at the mask, if any */ + masklen = (md == '/') ? atoi(p) : 128; + if (masklen > 128 || masklen < 0) + errx(EX_DATAERR, "bad width \"%s\''", p); + else + n2mask(&d[1], masklen); + + APPLY_MASK(d, &d[1]) /* mask base address with mask */ + + /* find next separator */ + + if (md == '/') { /* find separator past the mask */ + p = strpbrk(p, ","); + if (p != NULL) + p++; + } + av = p; + + /* Check this entry */ + if (masklen == 0) { + /* + * 'any' turns the entire list into a NOP. + * 'not any' never matches, so it is removed from the + * list unless it is the only item, in which case we + * report an error. + */ + if (cmd->o.len & F_NOT && av == NULL && len == 0) + errx(EX_DATAERR, "not any never matches"); + continue; + } + + /* + * A single IP can be stored alone + */ + if (masklen == 128 && av == NULL && len == 0) { + len = F_INSN_SIZE(struct in6_addr); + break; + } + + /* Update length and pointer to arguments */ + len += F_INSN_SIZE(struct in6_addr)*2; + d += 2; + } /* end while */ + + /* + * Total length of the command, remember that 1 is the size of + * the base command. + */ + cmd->o.len |= len+1; + free(av); + return (1); +} + +/* + * fills command for ipv6 flow-id filtering + * note that the 20 bit flow number is stored in a array of u_int32_t + * it's supported lists of flow-id, so in the o.arg1 we store how many + * additional flow-id we want to filter, the basic is 1 + */ +void +fill_flow6( ipfw_insn_u32 *cmd, char *av ) +{ + u_int32_t type; /* Current flow number */ + u_int16_t nflow = 0; /* Current flow index */ + char *s = av; + cmd->d[0] = 0; /* Initializing the base number*/ + + while (s) { + av = strsep( &s, ",") ; + type = strtoul(av, &av, 0); + if (*av != ',' && *av != '\0') + errx(EX_DATAERR, "invalid ipv6 flow number %s", av); + if (type > 0xfffff) + errx(EX_DATAERR, "flow number out of range %s", av); + cmd->d[nflow] |= type; + nflow++; + } + if( nflow > 0 ) { + cmd->o.opcode = O_FLOW6ID; + cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow; + cmd->o.arg1 = nflow; + } + else { + errx(EX_DATAERR, "invalid ipv6 flow number %s", av); + } +} + +static ipfw_insn * +add_srcip6(ipfw_insn *cmd, char *av) +{ + + fill_ip6((ipfw_insn_ip6 *)cmd, av); + if (F_LEN(cmd) == 0) /* any */ + ; + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ + cmd->opcode = O_IP6_SRC_ME; + } else if (F_LEN(cmd) == + (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { + /* single IP, no mask*/ + cmd->opcode = O_IP6_SRC; + } else { /* addr/mask opt */ + cmd->opcode = O_IP6_SRC_MASK; + } + return cmd; +} + +static ipfw_insn * +add_dstip6(ipfw_insn *cmd, char *av) +{ + + fill_ip6((ipfw_insn_ip6 *)cmd, av); + if (F_LEN(cmd) == 0) /* any */ + ; + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ + cmd->opcode = O_IP6_DST_ME; + } else if (F_LEN(cmd) == + (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { + /* single IP, no mask*/ + cmd->opcode = O_IP6_DST; + } else { /* addr/mask opt */ + cmd->opcode = O_IP6_DST_MASK; + } + return cmd; +} + + /* * helper function to process a set of flags and set bits in the * appropriate masks. @@ -2404,7 +2961,6 @@ struct dn_pipe p; int i; char *end; - uint32_t a; void *par = NULL; memset(&p, 0, sizeof p); @@ -2466,16 +3022,15 @@ */ par = NULL; - p.fs.flow_mask.dst_ip = 0; - p.fs.flow_mask.src_ip = 0; - p.fs.flow_mask.dst_port = 0; - p.fs.flow_mask.src_port = 0; - p.fs.flow_mask.proto = 0; + bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask)); end = NULL; while (ac >= 1) { uint32_t *p32 = NULL; uint16_t *p16 = NULL; + uint32_t *p20 = NULL; + struct in6_addr *pa6 = NULL; + uint32_t a; tok = match_token(dummynet_params, *av); ac--; av++; @@ -2489,6 +3044,9 @@ p.fs.flow_mask.dst_port = ~0; p.fs.flow_mask.src_port = ~0; p.fs.flow_mask.proto = ~0; + n2mask(&(p.fs.flow_mask.dst_ip6), 128); + n2mask(&(p.fs.flow_mask.src_ip6), 128); + p.fs.flow_mask.flow_id6 = ~0; p.fs.flags_fs |= DN_HAVE_FLOW_MASK; goto end_mask; @@ -2500,6 +3058,18 @@ p32 = &p.fs.flow_mask.src_ip; break; + case TOK_DSTIP6: + pa6 = &(p.fs.flow_mask.dst_ip6); + break; + + case TOK_SRCIP6: + pa6 = &(p.fs.flow_mask.src_ip6); + break; + + case TOK_FLOWID: + p20 = &p.fs.flow_mask.flow_id6; + break; + case TOK_DSTPORT: p16 = &p.fs.flow_mask.dst_port; break; @@ -2519,7 +3089,8 @@ errx(EX_USAGE, "mask: value missing"); if (*av[0] == '/') { a = strtoul(av[0]+1, &end, 0); - a = (a == 32) ? ~0 : (1 << a) - 1; + if (pa6 == NULL) + a = (a == 32) ? ~0 : (1 << a) - 1; } else a = strtoul(av[0], &end, 0); if (p32 != NULL) { @@ -2529,6 +3100,17 @@ errx(EX_DATAERR, "port mask must be 16 bit"); *p16 = (uint16_t)a; + } else if (p20 != NULL) { + if (a > 0xfffff) + errx(EX_DATAERR, + "flow_id mask must be 20 bit"); + *p20 = (uint32_t)a; + } else if (pa6 != NULL) { + if (a < 0 || a > 128) + errx(EX_DATAERR, + "in6addr invalid mask len"); + else + n2mask(pa6, a); } else { if (a > 0xff) errx(EX_DATAERR, @@ -2852,21 +3434,25 @@ } static ipfw_insn * -add_proto(ipfw_insn *cmd, char *av) +add_proto(ipfw_insn *cmd, char *av, u_char *proto) { struct protoent *pe; - u_char proto = 0; + + *proto = IPPROTO_IP; if (!strncmp(av, "all", strlen(av))) ; /* same as "ip" */ - else if ((proto = atoi(av)) > 0) + else if ((*proto = atoi(av)) > 0) ; /* all done! */ else if ((pe = getprotobyname(av)) != NULL) - proto = pe->p_proto; + *proto = pe->p_proto; + else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6")) + *proto = IPPROTO_IPV6; else return NULL; - if (proto != IPPROTO_IP) - fill_cmd(cmd, O_PROTO, 0, proto); + if (*proto != IPPROTO_IP && *proto != IPPROTO_IPV6) + fill_cmd(cmd, O_PROTO, 0, *proto); + return cmd; } @@ -2917,6 +3503,42 @@ return NULL; } +static ipfw_insn * +add_src(ipfw_insn *cmd, char *av, u_char proto) +{ + struct in6_addr a; + + if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || + inet_pton(AF_INET6, av, &a)) + return add_srcip6(cmd, av); + /* XXX: should check for IPv4, not !IPv6 */ + if (proto == IPPROTO_IP || strcmp(av, "me") == 0 || + !inet_pton(AF_INET6, av, &a)) + return add_srcip(cmd, av); + if (strcmp(av, "any") != 0) + return cmd; + + return NULL; +} + +static ipfw_insn * +add_dst(ipfw_insn *cmd, char *av, u_char proto) +{ + struct in6_addr a; + + if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || + inet_pton(AF_INET6, av, &a)) + return add_dstip6(cmd, av); + /* XXX: should check for IPv4, not !IPv6 */ + if (proto == IPPROTO_IP || strcmp(av, "me") == 0 || + !inet_pton(AF_INET6, av, &a)) + return add_dstip(cmd, av); + if (strcmp(av, "any") != 0) + return cmd; + + return NULL; +} + /* * Parse arguments and assemble the microinstructions which make up a rule. * Rules are added into the 'rulebuf' and then copied in the correct order @@ -3251,7 +3873,7 @@ OR_START(get_proto); NOT_BLOCK; NEED1("missing protocol"); - if (add_proto(cmd, *av)) { + if (add_proto(cmd, *av, &proto)) { av++; ac--; if (F_LEN(cmd) == 0) /* plain IP */ proto = 0; @@ -3279,13 +3901,14 @@ OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); - if (add_srcip(cmd, *av)) { + if (add_src(cmd, *av, proto)) { ac--; av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd); } - } + } else + errx(EX_USAGE, "bad source address %s", *av); OR_BLOCK(source_ip); /* @@ -3314,13 +3937,14 @@ OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); - if (add_dstip(cmd, *av)) { + if (add_dst(cmd, *av, proto)) { ac--; av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; cmd = next_cmd(cmd); } - } + } else + errx( EX_USAGE, "bad destination address %s", *av); OR_BLOCK(dest_ip); /* @@ -3437,6 +4061,12 @@ fill_icmptypes((ipfw_insn_u32 *)cmd, *av); av++; ac--; break; + + case TOK_ICMP6TYPES: + NEED1("icmptypes requires list of types"); + fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av); + av++; ac--; + break; case TOK_IPTTL: NEED1("ipttl requires TTL"); @@ -3641,8 +4271,9 @@ case TOK_PROTO: NEED1("missing protocol"); - if (add_proto(cmd, *av)) { - proto = cmd->arg1; + if (add_proto(cmd, *av, &proto)) { + if (proto == IPPROTO_IPV6) + fill_cmd(cmd, O_IP6, 0, 0); ac--; av++; } else errx(EX_DATAERR, "invalid protocol ``%s''", @@ -3663,6 +4294,20 @@ } break; + case TOK_SRCIP6: + NEED1("missing source IP6"); + if (add_srcip6(cmd, *av)) { + ac--; av++; + } + break; + + case TOK_DSTIP6: + NEED1("missing destination IP6"); + if (add_dstip6(cmd, *av)) { + ac--; av++; + } + break; + case TOK_SRCPORT: NEED1("missing source port"); if (!strncmp(*av, "any", strlen(*av)) || @@ -3709,6 +4354,24 @@ case TOK_IPSEC: fill_cmd(cmd, O_IPSEC, 0, 0); + break; + + case TOK_IPV6: + fill_cmd(cmd, O_IP6, 0, 0); + ac--; av++; + break; + + case TOK_EXT6HDR: + fill_ext6hdr( cmd, *av ); + ac--; av++; + break; + + case TOK_FLOWID: + if (proto != IPPROTO_IPV6 ) + errx( EX_USAGE, "flow-id filter is active " + "only for ipv6 protocol\n"); + fill_flow6( (ipfw_insn_u32 *) cmd, *av ); + ac--; av++; break; case TOK_COMMENT: --- ../cleanup/sbin/ipfw/ipfw.8 Mon Dec 13 22:06:52 2004 +++ sbin/ipfw/ipfw.8 Mon Dec 20 13:28:03 2004 @@ -346,18 +346,18 @@ These places and variables are shown below, and it is important to have this picture in mind in order to design a correct ruleset. .Bd -literal -offset indent - ^ to upper layers V - | | - +----------->-----------+ - ^ V - [ip_input] [ip_output] net.inet.ip.fw.enable=1 - | | - ^ V -[ether_demux] [ether_output_frame] net.link.ether.ipfw=1 - | | - +-->--[bdg_forward]-->--+ net.link.ether.bridge_ipfw=1 - ^ V - | to devices | + ^ to upper layers V + | | + +----------->-----------+ + ^ V + [ip(6)_input] [ip(6)_output] net.inet.ip.fw.enable=1 + | | + ^ V + [ether_demux] [ether_output_frame] net.link.ether.ipfw=1 + | | + +-->--[bdg_forward]-->--+ net.link.ether.bridge_ipfw=1 + ^ V + | to devices | .Ed .Pp As can be noted from the above picture, the number of @@ -375,13 +375,17 @@ but the same packets will have the MAC header stripped off when .Nm is invoked from -.Cm ip_input() . +.Cm ip_input() +or +.Cm ip6_input() . .Pp Also note that each packet is always checked against the complete ruleset, irrespective of the place where the check occurs, or the source of the packet. If a rule contains some match patterns or actions which are not valid for the place of invocation (e.g.\& trying to match a MAC header within .Cm ip_input() +or +.Cm ip6_input() ), the match pattern will not match, but a .Cm not operator in front of such patterns @@ -448,7 +452,7 @@ .Bl -tag -width "Source and dest. addresses and ports" -offset XXX -compact .It Layer-2 header fields When available -.It IPv4 Protocol +.It IPv4 and IPv6 Protocol TCP, UDP, ICMP, etc. .It Source and dest. addresses and ports .It Direction @@ -461,6 +465,10 @@ fragment flag (non-zero IP offset), Time To Live .It IP options +.It IPv6 Extension headers +Fragmentation, Hop-by-Hop options, +source routing, IPSec options. +.It IPv6 Flow-ID .It Misc. TCP header fields TCP flags (SYN, FIN, ACK, RST, etc.), sequence number, acknowledgment number, @@ -468,6 +476,8 @@ .It TCP options .It ICMP types for ICMP packets +.It ICMP6 types +for ICMP6 packets .It User/group ID When the packet can be associated with a local socket. .It Divert status @@ -774,7 +784,7 @@ .Nm ipfw1 . In .Nm ipfw2 -any match pattern (including MAC headers, IPv4 protocols, +any match pattern (including MAC headers, IP protocols, addresses and ports) can be specified in the .Ar options section. @@ -783,11 +793,13 @@ .Bl -tag -width indent .It Ar proto : protocol | Cm { Ar protocol Cm or ... } .It Ar protocol : Oo Cm not Oc Ar protocol-name | protocol-number -An IPv4 protocol specified by number or name +An IP protocol specified by number or name (for a complete list see .Pa /etc/protocols ) . The -.Cm ip +.Cm ip , +.Cm ip6 , +.Cm ipv6 , or .Cm all keywords mean any protocol will match. @@ -808,7 +820,7 @@ with multiple addresses) is provided for convenience only and its use is discouraged. .It Ar addr : Oo Cm not Oc Bro -.Cm any | me | +.Cm any | me | me6 .Cm table Ns Pq Ar number Ns Op , Ns Ar value .Ar | addr-list | addr-set .Brc @@ -816,10 +828,12 @@ matches any IP address. .It Cm me matches any IP address configured on an interface in the system. +.It Cm me6 +matches any IPv6 address configured on an interface in the system. The address list is evaluated at the time the packet is analysed. .It Cm table Ns Pq Ar number Ns Op , Ns Ar value -Matches any IP address for which an entry exists in the lookup table +Matches any IPv4 address for which an entry exists in the lookup table .Ar number . If an optional 32-bit unsigned .Ar value @@ -886,6 +900,30 @@ will match the following IP addresses: .br 1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 . +.It Ar addr6-list : ip6-addr Ns Op Ns , Ns Ar addr6-list +.It Ar ip6-addr : +A host or subnet specified one of the following ways: +.Pp +.Bl -tag -width indent +.It Ar numeric-ip | hostname +Matches a single IPv6 address as allowed by +.Xr inet_pton 3 +or a hostname. +Hostnames are resolved at the time the rule is added to the firewall +list. +.It Ar addr Ns / Ns Ar masklen +Matches all IPv6 addresses with base +.Ar addr +(specified as allowed by +.Xr inet_pton +or a hostname) +and mask width of +.Cm masklen +bits. +.El +.Pp +No support for sets of IPv6 addresses is provided because IPv6 addresses +are typically random past the initial prefix. .It Ar ports : Bro Ar port | port Ns \&- Ns Ar port Ns Brc Ns Op , Ns Ar ports For protocols which support port numbers (such as TCP and UDP), optional .Cm ports @@ -954,13 +992,36 @@ Matches only packets going from a divert socket back outward to the IP stack output for delivery. .It Cm dst-ip Ar ip-address -Matches IP packets whose destination IP is one of the address(es) +Matches IPv4 packets whose destination IP is one of the address(es) +specified as argument. +.It Bro Cm dst-ip6 | dst-ipv6 Brc Ar ip6-address +Matches IPv6 packets whose destination IP is one of the address(es) specified as argument. .It Cm dst-port Ar ports Matches IP packets whose destination port is one of the port(s) specified as argument. .It Cm established Matches TCP packets that have the RST or ACK bits set. +.It Cm ext6hdr Ar header +Matches IPv6 packets containing the extended header given by +.Ar header . +Supported headers are: +.Pp +Fragment, +.Pq Cm frag , +Hop-to-hop options +.Pq Cm hopopt , +Source routing +.Pq Cm route , +IPSec authentication headers +.Pq Cm ah , +and IPSec encapsulated security payload headers +.Pq Cm esp . +.It Cm flow-id Ar labels +Matches IPv6 packets containing any of the flow labels given in +.Ar labels . +.Ar labels +is a comma seperate list of numeric flow labels. .It Cm frag Matches packets that are fragments and not the first fragment of an IP datagram. @@ -1015,6 +1076,12 @@ .Pq Cm 17 and address mask reply .Pq Cm 18 . +.It Cm icmp6types Ar types +Matches ICMP6 packets whose ICMP6 type is in the list of +.Ar types . +The list may be specified as any combination of +individual types (numeric) separated by commas. +.Em Ranges are not allowed. .It Cm in | out Matches incoming or outgoing packets, respectively. .Cm in @@ -1025,7 +1092,7 @@ is implemented as .Cm not in Ns No ). .It Cm ipid Ar id-list -Matches IP packets whose +Matches IPv4 packets whose .Cm ip_id field has value included in .Ar id-list , @@ -1040,7 +1107,7 @@ specified in the same way as .Ar ports . .It Cm ipoptions Ar spec -Matches packets whose IP header contains the comma separated list of +Matches packets whose IPv4 header contains the comma separated list of options specified in .Ar spec . The supported IP options are: @@ -1057,7 +1124,7 @@ with a .Ql \&! . .It Cm ipprecedence Ar precedence -Matches IP packets whose precedence field is equal to +Matches IPv4 packets whose precedence field is equal to .Ar precedence . .It Cm ipsec Matches packets that have IPSEC history associated with them @@ -1079,7 +1146,7 @@ .Cm ipsec flag. .It Cm iptos Ar spec -Matches IP packets whose +Matches IPv4 packets whose .Cm tos field contains the comma separated list of service types specified in @@ -1100,7 +1167,7 @@ with a .Ql \&! . .It Cm ipttl Ar ttl-list -Matches IP packets whose time to live is included in +Matches IPv4 packets whose time to live is included in .Ar ttl-list , which is either a single value or a list of values or ranges specified in the same way as @@ -1128,6 +1195,8 @@ One or more of source and destination addresses and ports can be specified. +Currently, +only IPv4 flows are supported. .It Cm { MAC | mac } Ar dst-mac src-mac Match packets with a given .Ar dst-mac @@ -1180,7 +1249,7 @@ .Cm -N option is used, in which case symbolic resolution will be attempted). .It Cm proto Ar protocol -Matches packets with the corresponding IPv4 protocol. +Matches packets with the corresponding IP protocol. .It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any Matches packets received, transmitted or going through, respectively, the interface specified by exact name @@ -1228,8 +1297,11 @@ This is the short form of .Dq Li tcpflags\ syn,!ack . .It Cm src-ip Ar ip-address -Matches IP packets whose source IP is one of the address(es) -specified as argument. +Matches IPv4 packets whose source IP is one of the address(es) +specified as an argument. +.It Cm src-ip6 Ar ip6-address +Matches IPv6 packets whose source IP is one of the address(es) +specified as an argument. .It Cm src-port Ar ports Matches IP packets whose source port is one of the port(s) specified as argument. @@ -1356,7 +1428,7 @@ .Sh LOOKUP TABLES Lookup tables are useful to handle large sparse address sets, typically from a hundred to several thousands of entries. -There could be 128 different lookup tables, numbered 0 to 127. +There may be up to 128 different lookup tables, numbered 0 to 127. .Pp Each entry is represented by an .Ar addr Ns Op / Ns Ar masklen @@ -1390,6 +1462,8 @@ Internally, each table is stored in a Radix tree, the same way as the routing table (see .Xr route 4 ) . +.Pp +Lookup tables currently support IPv4 addresses only. .Sh SETS OF RULES Each rule belongs to one of 32 different .Em sets @@ -1662,9 +1736,12 @@ Available mask specifiers are a combination of one or more of the following: .Pp .Cm dst-ip Ar mask , +.Cm dst-ip6 Ar mask , .Cm src-ip Ar mask , +.Cm src-ip6 Ar mask , .Cm dst-port Ar mask , .Cm src-port Ar mask , +.Cm flow-id Ar mask , .Cm proto Ar mask or .Cm all , @@ -1735,6 +1812,14 @@ thresholds are in bytes (defaults to 1500, must be greater than zero). .El .El +.Pp +When used with IPv6 data, dummynet currently has several limitations. +First, debug.mpsafenet=0 must be set. +Second, the information necessicary to route link-local packets to an +interface is not avalable after processing by dummynet so those packets +are dropped in the output path. +Care should be taken to insure that link-local packets are not passed to +dummynet. .Sh CHECKLIST Here are some important points to consider when designing your rules: @@ -2060,6 +2145,9 @@ .Nm dummynet pipes/queues is not supported: .Cm noerror . +.It IPv6 Support +There was no IPv6 support in +.Nm ipfw1 . .El .Sh EXAMPLES There are far too many possible uses of @@ -2336,6 +2424,8 @@ .Xr sysctl 8 , .Xr syslogd 8 .Sh BUGS +Use of dummynet with IPv6 requires that debug.mpsafenet be set to 0. +.Pp The syntax has grown over the years and sometimes it might be confusing. Unfortunately, backward compatibility prevents cleaning up mistakes made in the definition of the syntax. @@ -2364,6 +2454,8 @@ applied, making the order of .Cm divert rules in the rule sequence very important. +.Pp +Dummynet drops all packets with IPv6 link-local addresses. .Sh AUTHORS .An Ugen J. S. Antsilevich , .An Poul-Henning Kamp , --- ../cleanup/sys/netinet/ip_dummynet.c Mon Nov 22 12:52:14 2004 +++ sys/netinet/ip_dummynet.c Mon Nov 22 14:18:40 2004 @@ -77,6 +77,9 @@ #include /* for struct arpcom */ #include +#include /* for ip6_input, ip6_output prototypes */ +#include + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) @@ -461,6 +464,14 @@ ip_input(m) ; break ; + case DN_TO_IP6_IN: + ip6_input(m) ; + break ; + + case DN_TO_IP6_OUT: + (void)ip6_output(m, NULL, NULL, pkt->flags, NULL, NULL, NULL); + break ; + case DN_TO_BDG_FWD : /* * The bridge requires/assumes the Ethernet header is @@ -898,37 +909,80 @@ { int i = 0 ; /* we need i and q for new allocations */ struct dn_flow_queue *q, *prev; + int is_v6 = IS_IP6_FLOW_ID(id); if ( !(fs->flags_fs & DN_HAVE_FLOW_MASK) ) q = fs->rq[0] ; else { - /* first, do the masking */ - id->dst_ip &= fs->flow_mask.dst_ip ; - id->src_ip &= fs->flow_mask.src_ip ; + /* first, do the masking, then hash */ id->dst_port &= fs->flow_mask.dst_port ; id->src_port &= fs->flow_mask.src_port ; id->proto &= fs->flow_mask.proto ; id->flags = 0 ; /* we don't care about this one */ - /* then, hash function */ - i = ( (id->dst_ip) & 0xffff ) ^ - ( (id->dst_ip >> 15) & 0xffff ) ^ - ( (id->src_ip << 1) & 0xffff ) ^ - ( (id->src_ip >> 16 ) & 0xffff ) ^ - (id->dst_port << 1) ^ (id->src_port) ^ - (id->proto ); + if (is_v6) { + APPLY_MASK(&id->dst_ip6, &fs->flow_mask.dst_ip6); + APPLY_MASK(&id->src_ip6, &fs->flow_mask.src_ip6); + id->flow_id6 &= fs->flow_mask.flow_id6; + + i = ((id->dst_ip6.__u6_addr.__u6_addr32[0]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3]) & 0xffff)^ + + ((id->dst_ip6.__u6_addr.__u6_addr32[0] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3] >> 15) & 0xffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] << 1) & 0xfffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] << 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] << 16) & 0xffff)^ + + (id->dst_port << 1) ^ (id->src_port) ^ + (id->proto ) ^ + (id->flow_id6); + } else { + id->dst_ip &= fs->flow_mask.dst_ip ; + id->src_ip &= fs->flow_mask.src_ip ; + + i = ( (id->dst_ip) & 0xffff ) ^ + ( (id->dst_ip >> 15) & 0xffff ) ^ + ( (id->src_ip << 1) & 0xffff ) ^ + ( (id->src_ip >> 16 ) & 0xffff ) ^ + (id->dst_port << 1) ^ (id->src_port) ^ + (id->proto ); + } i = i % fs->rq_size ; /* finally, scan the current list for a match */ searches++ ; for (prev=NULL, q = fs->rq[i] ; q ; ) { search_steps++; - if (id->dst_ip == q->id.dst_ip && + if (is_v6 && + IN6_ARE_ADDR_EQUAL(&id->dst_ip6,&q->id.dst_ip6) && + IN6_ARE_ADDR_EQUAL(&id->src_ip6,&q->id.src_ip6) && + id->dst_port == q->id.dst_port && + id->src_port == q->id.src_port && + id->proto == q->id.proto && + id->flags == q->id.flags && + id->flow_id6 == q->id.flow_id6) + break ; /* found */ + + if (!is_v6 && id->dst_ip == q->id.dst_ip && id->src_ip == q->id.src_ip && id->dst_port == q->id.dst_port && id->src_port == q->id.src_port && id->proto == q->id.proto && id->flags == q->id.flags) break ; /* found */ - else if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { + + /* No match. Check if we can expire the entry */ + if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { /* entry is idle and not in any heap, expire it */ struct dn_flow_queue *old_q = q ; @@ -1202,8 +1256,9 @@ pkt->dn_dir = dir ; pkt->ifp = fwa->oif; - if (dir == DN_TO_IP_OUT) + if (dir == DN_TO_IP_OUT || dir == DN_TO_IP6_OUT) pkt->flags = fwa->flags; + if (q->head == NULL) q->head = m; else @@ -2017,7 +2072,7 @@ ip_dn_init(void) { if (bootverbose) - printf("DUMMYNET initialized (011031)\n"); + printf("DUMMYNET with IPv6 initialized (040826)\n"); DUMMYNET_LOCK_INIT(); --- ../cleanup/sys/netinet/ip_dummynet.h Thu Aug 26 18:01:22 2004 +++ sys/netinet/ip_dummynet.h Thu Sep 16 22:03:45 2004 @@ -124,10 +124,13 @@ #define DN_TO_BDG_FWD 3 #define DN_TO_ETH_DEMUX 4 #define DN_TO_ETH_OUT 5 +#define DN_TO_IP6_IN 6 +#define DN_TO_IP6_OUT 7 dn_key output_time; /* when the pkt is due for delivery */ struct ifnet *ifp; /* interface, for ip_output */ int flags ; /* flags, for ip_output (IPv6 ?) */ + struct _ip6dn_args ip6opt; /* XXX ipv6 options */ }; #endif /* _KERNEL */ --- ../cleanup/sys/netinet/ip_fw.h Wed Oct 6 18:30:46 2004 +++ sys/netinet/ip_fw.h Sat Oct 23 17:44:48 2004 @@ -137,11 +137,31 @@ O_ALTQ, /* u32 = altq classif. qid */ O_DIVERTED, /* arg1=bitmap (1:loop, 2:out) */ O_TCPDATALEN, /* arg1 = tcp data len */ + O_IP6_SRC, /* address without mask */ + O_IP6_SRC_ME, /* my addresses */ + O_IP6_SRC_MASK, /* address with the mask */ + O_IP6_DST, + O_IP6_DST_ME, + O_IP6_DST_MASK, + O_FLOW6ID, /* for flow id tag in the ipv6 pkt */ + O_ICMP6TYPE, /* icmp6 packet type filtering */ + O_EXT_HDR, /* filtering for ipv6 extension header */ + O_IP6, O_LAST_OPCODE /* not an opcode! */ }; /* + * The extension header are filtered only for presence using a bit + * vector with a flag for each header. + */ +#define EXT_FRAGMENT 0x1 +#define EXT_HOPOPTS 0x2 +#define EXT_ROUTING 0x4 +#define EXT_AH 0x8 +#define EXT_ESP 0x10 + +/* * Template for instructions. * * ipfw_insn is used for all instructions which require no operands, @@ -285,6 +305,30 @@ u_int32_t log_left; /* how many left to log */ } ipfw_insn_log; +/* Apply ipv6 mask on ipv6 addr */ +#define APPLY_MASK(addr,mask) \ + (addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \ + (addr)->__u6_addr.__u6_addr32[1] &= (mask)->__u6_addr.__u6_addr32[1]; \ + (addr)->__u6_addr.__u6_addr32[2] &= (mask)->__u6_addr.__u6_addr32[2]; \ + (addr)->__u6_addr.__u6_addr32[3] &= (mask)->__u6_addr.__u6_addr32[3]; + +/* Structure for ipv6 */ +typedef struct _ipfw_insn_ip6 { + ipfw_insn o; + struct in6_addr addr6; + struct in6_addr mask6; +} ipfw_insn_ip6; + +/* Used to support icmp6 types */ +typedef struct _ipfw_insn_icmp6 { + ipfw_insn o; + uint32_t d[7]; /* XXX This number si related to the netinet/icmp6.h + * define ICMP6_MAXTYPE + * as follows: n = ICMP6_MAXTYPE/32 + 1 + * Actually is 203 + */ +} ipfw_insn_icmp6; + /* * Here we have the structure representing an ipfw rule. * @@ -348,8 +392,14 @@ u_int16_t src_port; u_int8_t proto; u_int8_t flags; /* protocol-specific flags */ + uint8_t addr_type; /* 4 = ipv4, 6 = ipv6, 1=ether ? */ + struct in6_addr dst_ip6; /* could also store MAC addr! */ + struct in6_addr src_ip6; + u_int32_t flow_id6; }; +#define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6) + /* * Dynamic ipfw rule. */ @@ -424,6 +474,21 @@ #define IP_FW_DIVERT_OUTPUT_FLAG 0x00100000 /* + * Structure for collecting parameters to dummynet for ip6_output forwarding + */ +struct _ip6dn_args { + struct ip6_pktopts *opt_or; + struct route_in6 ro_or; + int flags_or; + struct ip6_moptions *im6o_or; + struct ifnet *origifp_or; + struct ifnet *ifp_or; + struct sockaddr_in6 dst_or; + u_long mtu_or; + struct route_in6 ro_pmtu_or; +}; + +/* * Arguments for calling ipfw_chk() and dummynet_io(). We put them * all into a structure because this way it is easier and more * efficient to pass variables around and extend the interface. @@ -440,6 +505,8 @@ struct ipfw_flow_id f_id; /* grabbed from IP header */ u_int32_t retval; struct inpcb *inp; + + struct _ip6dn_args dummypar; /* dummynet->ip6_output */ }; /* --- ../cleanup/sys/netinet/ip_fw2.c Mon Dec 13 22:07:04 2004 +++ sys/netinet/ip_fw2.c Mon Dec 20 13:28:11 2004 @@ -83,6 +83,9 @@ #include #endif +#include +#include + #include /* XXX for ETHERTYPE_IP */ #include /* XXX for in_cksum */ @@ -321,6 +324,7 @@ #define TCP(p) ((struct tcphdr *)(p)) #define UDP(p) ((struct udphdr *)(p)) #define ICMP(p) ((struct icmp *)(p)) +#define ICMP6(p) ((struct icmp6_hdr *)(p)) static __inline int icmptype_match(struct icmp *icmp, ipfw_insn_u32 *cmd) @@ -551,6 +555,83 @@ return 1; } +/* + * ipv6 specific rules here... + */ +static __inline int +icmp6type_match (int type, ipfw_insn_u32 *cmd) +{ + return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) ); +} + +static int +flow6id_match( int curr_flow, ipfw_insn_u32 *cmd ) +{ + int i; + for (i=0; i <= cmd->o.arg1; ++i ) + if (curr_flow == cmd->d[i] ) + return 1; + return 0; +} + +/* support for IP6_*_ME opcodes */ +static int +search_ip6_addr_net (struct in6_addr * ip6_addr) +{ + struct ifnet *mdc; + struct ifaddr *mdc2; + struct in6_ifaddr *fdm; + struct in6_addr copia; + + TAILQ_FOREACH(mdc, &ifnet, if_link) + for (mdc2 = mdc->if_addrlist.tqh_first; mdc2; + mdc2 = mdc2->ifa_list.tqe_next) { + if (!mdc2->ifa_addr) + continue; + if (mdc2->ifa_addr->sa_family == AF_INET6) { + fdm = (struct in6_ifaddr *)mdc2; + copia = fdm->ia_addr.sin6_addr; + /* need for leaving scope_id in the sock_addr */ + in6_clearscope(&copia); + if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia)) + return 1; + } + } + return 0; +} + +static int +verify_rev_path6(struct in6_addr *src, struct ifnet *ifp) +{ + static struct route_in6 ro; + struct sockaddr_in6 *dst; + + dst = (struct sockaddr_in6 * )&(ro.ro_dst); + + if ( !(IN6_ARE_ADDR_EQUAL (src, &dst->sin6_addr) )) { + bzero(dst, sizeof(*dst)); + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(*dst); + dst->sin6_addr = *src; + rtalloc_ign((struct route *)&ro, RTF_CLONING); + } + if ((ro.ro_rt == NULL) || (ifp == NULL) || + (ro.ro_rt->rt_ifp->if_index != ifp->if_index)) + return 0; + return 1; +} +static __inline int +hash_packet6(struct ipfw_flow_id *id) +{ + u_int32_t i; + i= (id->dst_ip6.__u6_addr.__u6_addr32[0]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[1]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ + (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ + (id->dst_port) ^ (id->src_port) ^ (id->flow_id6); + return i; +} +/* end of ipv6 opcodes */ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */ @@ -761,7 +842,8 @@ { u_int32_t i; - i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); + i = IS_IP6_FLOW_ID(id) ? hash_packet6(id): + (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); i &= (curr_dyn_buckets - 1); return i; } @@ -900,19 +982,40 @@ } if (pkt->proto == q->id.proto && q->dyn_type != O_LIMIT_PARENT) { - if (pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip && + if (IS_IP6_FLOW_ID(pkt)) { + if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.src_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.dst_ip6)) && pkt->src_port == q->id.src_port && pkt->dst_port == q->id.dst_port ) { dir = MATCH_FORWARD; break; - } - if (pkt->src_ip == q->id.dst_ip && - pkt->dst_ip == q->id.src_ip && - pkt->src_port == q->id.dst_port && - pkt->dst_port == q->id.src_port ) { - dir = MATCH_REVERSE; - break; + } + if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.dst_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.src_ip6)) && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port ) { + dir = MATCH_REVERSE; + break; + } + } else { + if (pkt->src_ip == q->id.src_ip && + pkt->dst_ip == q->id.dst_ip && + pkt->src_port == q->id.src_port && + pkt->dst_port == q->id.dst_port ) { + dir = MATCH_FORWARD; + break; + } + if (pkt->src_ip == q->id.dst_ip && + pkt->dst_ip == q->id.src_ip && + pkt->src_port == q->id.dst_port && + pkt->dst_port == q->id.src_port ) { + dir = MATCH_REVERSE; + break; + } } } next: @@ -1110,15 +1213,25 @@ IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v) { + int is_v6 = IS_IP6_FLOW_ID(pkt); i = hash_packet( pkt ); for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next) if (q->dyn_type == O_LIMIT_PARENT && rule== q->rule && pkt->proto == q->id.proto && - pkt->src_ip == q->id.src_ip && - pkt->dst_ip == q->id.dst_ip && pkt->src_port == q->id.src_port && - pkt->dst_port == q->id.dst_port) { + pkt->dst_port == q->id.dst_port && + ( + (is_v6 && + IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), + &(q->id.src_ip6)) && + IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), + &(q->id.dst_ip6))) || + (!is_v6 && + pkt->src_ip == q->id.src_ip && + pkt->dst_ip == q->id.dst_ip) + ) + ) { q->expire = time_second + dyn_short_lifetime; DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);) return q; @@ -1192,10 +1305,17 @@ id.dst_port = id.src_port = 0; id.proto = args->f_id.proto; - if (limit_mask & DYN_SRC_ADDR) - id.src_ip = args->f_id.src_ip; - if (limit_mask & DYN_DST_ADDR) - id.dst_ip = args->f_id.dst_ip; + if (IS_IP6_FLOW_ID (&(args->f_id))) { + if (limit_mask & DYN_SRC_ADDR) + id.src_ip6 = args->f_id.src_ip6; + if (limit_mask & DYN_DST_ADDR) + id.dst_ip6 = args->f_id.dst_ip6; + } else { + if (limit_mask & DYN_SRC_ADDR) + id.src_ip = args->f_id.src_ip; + if (limit_mask & DYN_DST_ADDR) + id.dst_ip = args->f_id.dst_ip; + } if (limit_mask & DYN_SRC_PORT) id.src_port = args->f_id.src_port; if (limit_mask & DYN_DST_PORT) @@ -1822,6 +1942,10 @@ * ulp is NULL if not found. */ void *ulp = NULL; /* upper layer protocol pointer. */ + /* XXX ipv6 variables */ + int is_ipv6 = 0; + u_int16_t ext_hd = 0; /* bits vector for extension header filtering */ + /* end of ipv6 variables */ if (m->m_flags & M_SKIP_FIREWALL) return 0; /* accept */ @@ -1846,15 +1970,95 @@ p = (mtod(m, char *) + (len)); \ } while (0) - /* Identify IP packets and fill up veriables. */ - if (pktlen >= sizeof(struct ip) && + /* Identify IP packets and fill up variables. */ + if (pktlen >= sizeof(struct ip6_hdr) && + (args->eh == NULL || ntohs(args->eh->ether_type)==ETHERTYPE_IPV6) && + mtod(m, struct ip *)->ip_v == 6) { + is_ipv6 = 1; + args->f_id.addr_type = 6; + hlen = sizeof(struct ip6_hdr); + proto = mtod(m, struct ip6_hdr *)->ip6_nxt; + + /* Search extension headers to find upper layer protocols */ + while (ulp == NULL) { + switch (proto) { + case IPPROTO_ICMPV6: + PULLUP_TO(hlen, ulp, struct icmp6_hdr); + args->f_id.flags = ICMP6(ulp)->icmp6_type; + break; + + case IPPROTO_TCP: + PULLUP_TO(hlen, ulp, struct tcphdr); + dst_port = TCP(ulp)->th_dport; + src_port = TCP(ulp)->th_sport; + args->f_id.flags = TCP(ulp)->th_flags; + break; + + case IPPROTO_UDP: + PULLUP_TO(hlen, ulp, struct udphdr); + dst_port = UDP(ulp)->uh_dport; + src_port = UDP(ulp)->uh_sport; + break; + + case IPPROTO_HOPOPTS: + PULLUP_TO(hlen, ulp, struct ip6_hbh); + ext_hd |= EXT_HOPOPTS; + hlen += sizeof(struct ip6_hbh); + proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; + ulp = NULL; + break; + + case IPPROTO_ROUTING: + PULLUP_TO(hlen, ulp, struct ip6_rthdr); + ext_hd |= EXT_ROUTING; + hlen += sizeof(struct ip6_rthdr); + proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; + ulp = NULL; + break; + + case IPPROTO_FRAGMENT: + PULLUP_TO(hlen, ulp, struct ip6_frag); + ext_hd |= EXT_FRAGMENT; + hlen += sizeof (struct ip6_frag); + proto = ((struct ip6_frag *)ulp)->ip6f_nxt; + offset = 1; + ulp = NULL; /* XXX is it correct ? */ + break; + + case IPPROTO_AH: + case IPPROTO_NONE: + case IPPROTO_ESP: + PULLUP_TO(hlen, ulp, struct ip6_ext); + if (proto == IPPROTO_AH) + ext_hd |= EXT_AH; + else if (proto == IPPROTO_ESP) + ext_hd |= EXT_ESP; + hlen += ((struct ip6_ext *)ulp)->ip6e_len + + sizeof (struct ip6_ext); + proto = ((struct ip6_ext *)ulp)->ip6e_nxt; + ulp = NULL; + break; + + default: + printf( "IPFW2: IPV6 - Unknown Extension Header (%d)\n", + proto); + return 0; /* deny */ + break; + } /*switch */ + } + args->f_id.src_ip6 = mtod(m,struct ip6_hdr *)->ip6_src; + args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst; + args->f_id.src_ip = 0; + args->f_id.dst_ip = 0; + args->f_id.flow_id6 = ntohs(mtod(m, struct ip6_hdr *)->ip6_flow); + /* hlen != 0 is used to detect ipv4 packets, so clear it now */ + hlen = 0; + } else if (pktlen >= sizeof(struct ip) && (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && mtod(m, struct ip *)->ip_v == 4) { ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; -#ifdef NOTYET args->f_id.addr_type = 4; -#endif /* * Collect parameters into local variables for faster matching. @@ -2018,11 +2222,13 @@ case O_JAIL: /* * We only check offset == 0 && proto != 0, - * as this ensures that we have an IPv4 + * as this ensures that we have a * packet with the ports info. */ if (offset!=0) break; + if (is_ipv6) /* XXX to be fixed later */ + break; if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) match = check_uidgid( @@ -2077,7 +2283,7 @@ break; case O_FRAG: - match = (hlen > 0 && offset != 0); + match = (offset != 0); break; case O_IN: /* "out" is "not in" */ @@ -2186,7 +2392,7 @@ case O_IP_DSTPORT: /* * offset == 0 && proto != 0 is enough - * to guarantee that we have an IPv4 + * to guarantee that we have a * packet with port info. */ if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP) @@ -2209,12 +2415,22 @@ icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); break; + case O_ICMP6TYPE: + match = is_ipv6 && offset == 0 && + proto==IPPROTO_ICMPV6 && + icmp6type_match( + ICMP6(ulp)->icmp6_type, + (ipfw_insn_u32 *)cmd); + break; + case O_IPOPT: - match = (hlen > 0 && ipopts_match(ip, cmd) ); + match = (hlen > 0 && + ipopts_match(mtod(m, struct ip *), cmd) ); break; case O_IPVER: - match = (hlen > 0 && cmd->arg1 == ip->ip_v); + match = (hlen > 0 && + cmd->arg1 == mtod(m, struct ip *)->ip_v); break; case O_IPID: @@ -2228,9 +2444,9 @@ if (cmd->opcode == O_IPLEN) x = ip_len; else if (cmd->opcode == O_IPTTL) - x = ip->ip_ttl; + x = mtod(m, struct ip *)->ip_ttl; else /* must be IPID */ - x = ntohs(ip->ip_id); + x = ntohs(mtod(m, struct ip *)->ip_id); if (cmdlen == 1) { match = (cmd->arg1 == x); break; @@ -2245,12 +2461,12 @@ case O_IPPRECEDENCE: match = (hlen > 0 && - (cmd->arg1 == (ip->ip_tos & 0xe0)) ); + (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xe0)) ); break; case O_IPTOS: match = (hlen > 0 && - flags_match(cmd, ip->ip_tos)); + flags_match(cmd, mtod(m, struct ip *)->ip_tos)); break; case O_TCPDATALEN: @@ -2348,8 +2564,12 @@ case O_VERREVPATH: /* Outgoing packets automatically pass/match */ - match = (hlen > 0 && ((oif != NULL) || + /* XXX BED: verify_path was verify_rev_path in the diff... */ + match = ((oif != NULL) || (m->m_pkthdr.rcvif == NULL) || + (is_ipv6 ? + verify_rev_path6(&(args->f_id.src_ip6), + m->m_pkthdr.rcvif) : verify_path(src_ip, m->m_pkthdr.rcvif))); break; @@ -2380,6 +2600,60 @@ /* otherwise no match */ break; + case O_IP6_SRC: + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + + case O_IP6_DST: + match = is_ipv6 && + IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6, + &((ipfw_insn_ip6 *)cmd)->addr6); + break; + case O_IP6_SRC_MASK: + if (is_ipv6) { + ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; + struct in6_addr p = args->f_id.src_ip6; + + APPLY_MASK(&p, &te->mask6); + match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); + } + break; + + case O_IP6_DST_MASK: + if (is_ipv6) { + ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; + struct in6_addr p = args->f_id.dst_ip6; + + APPLY_MASK(&p, &te->mask6); + match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); + } + break; + + case O_IP6_SRC_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6); + break; + + case O_IP6_DST_ME: + match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6); + break; + + case O_FLOW6ID: + match = is_ipv6 && + flow6id_match(args->f_id.flow_id6, + (ipfw_insn_u32 *) cmd); + break; + + case O_EXT_HDR: + match = is_ipv6 && + (ext_hd & ((ipfw_insn *) cmd)->arg1); + break; + + case O_IP6: + match = is_ipv6; + break; + /* * The second set of opcodes represents 'actions', * i.e. the terminal part of a rule once the packet @@ -3013,6 +3287,10 @@ case O_VERSRCREACH: case O_ANTISPOOF: case O_IPSEC: + case O_IP6_SRC_ME: + case O_IP6_DST_ME: + case O_EXT_HDR: + case O_IP6: if (cmdlen != F_INSN_SIZE(ipfw_insn)) goto bad_size; break; @@ -3151,6 +3429,29 @@ return EINVAL; } break; + case O_IP6_SRC: + case O_IP6_DST: + if (cmdlen != F_INSN_SIZE(struct in6_addr) + + F_INSN_SIZE(ipfw_insn)) + goto bad_size; + break; + + case O_FLOW6ID: + if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + + ((ipfw_insn_u32 *)cmd)->o.arg1) + goto bad_size; + break; + + case O_IP6_SRC_MASK: + case O_IP6_DST_MASK: + if ( !(cmdlen & 1) || cmdlen > 127) + goto bad_size; + break; + case O_ICMP6TYPE: + if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) + goto bad_size; + break; + default: printf("ipfw: opcode %d, unknown opcode\n", cmd->opcode); @@ -3548,7 +3849,7 @@ } ip_fw_default_rule = layer3_chain.rules; - printf("ipfw2 initialized, divert %s, " + printf("ipfw2 (+ipv6) initialized, divert %s, " "rule-based forwarding " #ifdef IPFIREWALL_FORWARD "enabled, " --- ../cleanup/sys/netinet/ip_fw_pfil.c Mon Dec 13 22:07:04 2004 +++ sys/netinet/ip_fw_pfil.c Mon Dec 20 13:28:12 2004 @@ -30,6 +30,7 @@ #include "opt_ipfw.h" #include "opt_ipdn.h" #include "opt_inet.h" +#include "opt_inet6.h" #ifndef INET #error IPFIREWALL requires INET. #endif /* INET */ @@ -116,7 +117,10 @@ goto pass; if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { - ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args); + if (mtod(*m0, struct ip *)->ip_v == 4) + ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args); + else if (mtod(*m0, struct ip *)->ip_v == 6) + ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP6_IN, &args); *m0 = NULL; return 0; /* packet consumed */ } @@ -202,7 +206,10 @@ goto pass; if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { - ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args); + if (mtod(*m0, struct ip *)->ip_v == 4) + ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args); + else if (mtod(*m0, struct ip *)->ip_v == 6) + ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP6_OUT, &args); *m0 = NULL; return 0; /* packet consumed */ } @@ -337,6 +344,9 @@ ipfw_hook(void) { struct pfil_head *pfh_inet; +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif if (ipfw_pfil_hooked) return EEXIST; @@ -344,9 +354,18 @@ pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return ENOENT; +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) + return ENOENT; +#endif pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); +#ifdef INET6 + pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); + pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); +#endif return 0; } @@ -355,6 +374,9 @@ ipfw_unhook(void) { struct pfil_head *pfh_inet; +#ifdef INET6 + struct pfil_head *pfh_inet6; +#endif if (!ipfw_pfil_hooked) return ENOENT; @@ -362,9 +384,18 @@ pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return ENOENT; +#ifdef INET6 + pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (pfh_inet6 == NULL) + return ENOENT; +#endif pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); +#ifdef INET6 + pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); + pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); +#endif return 0; } --- ../cleanup/sys/netinet6/ip6_output.c Wed Oct 6 18:30:54 2004 +++ sys/netinet6/ip6_output.c Sat Oct 23 17:45:02 2004 @@ -78,6 +78,7 @@ #include #include +#include #include #include @@ -167,6 +168,7 @@ int hlen, tlen, len, off; struct route_in6 ip6route; struct sockaddr_in6 *dst; + struct in6_addr odst; int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; @@ -525,6 +527,7 @@ ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; +again: /* * If there is a cached route, * check that it is to the same destination @@ -938,11 +941,34 @@ if (inet6_pfil_hook.ph_busy_count == -1) goto passout; + odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_OUT, inp); if (error != 0 || m == NULL) goto done; ip6 = mtod(m, struct ip6_hdr *); + + /* See if destination IP address was changed by packet filter. */ + if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { + m->m_flags |= M_SKIP_FIREWALL; + /* If destination is now ourself drop to ip6_input(). */ + if (in6_localaddr(&ip6->ip6_dst)) { + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = loif; + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID; + error = netisr_queue(NETISR_IPV6, m); + goto done; + } else + goto again; /* Redo the routing table lookup. */ + } + + /* XXX: IPFIREWALL_FORWARD */ passout: /* Changed files: sbin/ipfw/ipfw2.c sbin/ipfw/ipfw.8 sys/netinet/ip_dummynet.c sys/netinet/ip_dummynet.h sys/netinet/ip_fw.h sys/netinet/ip_fw2.c sys/netinet/ip_fw_pfil.c sys/netinet6/ip6_output.c