summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbrooks <brooks@FreeBSD.org>2005-04-18 18:35:05 +0000
committerbrooks <brooks@FreeBSD.org>2005-04-18 18:35:05 +0000
commitf3ecaa630b5d676d2b43b5da90f46c294bd63836 (patch)
treeeb64e48417a6452c61a02673f46e16a8590fd13a /sys
parent6dfe72cfb93de2ed0bca56214d34e7154c93b420 (diff)
downloadFreeBSD-src-f3ecaa630b5d676d2b43b5da90f46c294bd63836.zip
FreeBSD-src-f3ecaa630b5d676d2b43b5da90f46c294bd63836.tar.gz
Add IPv6 support to IPFW and Dummynet.
Submitted by: Mariano Tortoriello and Raffaele De Lorenzo (via luigi)
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/ip_dummynet.c83
-rw-r--r--sys/netinet/ip_dummynet.h3
-rw-r--r--sys/netinet/ip_fw.h67
-rw-r--r--sys/netinet/ip_fw2.c365
-rw-r--r--sys/netinet/ip_fw_pfil.c35
-rw-r--r--sys/netinet6/ip6_output.c26
6 files changed, 531 insertions, 48 deletions
diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c
index 0a93a2f..ccea5ea6 100644
--- a/sys/netinet/ip_dummynet.c
+++ b/sys/netinet/ip_dummynet.c
@@ -77,6 +77,9 @@
#include <netinet/if_ether.h> /* for struct arpcom */
#include <net/bridge.h>
+#include <netinet/ip6.h> /* for ip6_input, ip6_output prototypes */
+#include <netinet6/ip6_var.h>
+
/*
* 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 @@ transmit_event(struct dn_pipe *pipe)
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 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id)
{
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 ;
@@ -1200,8 +1254,9 @@ dummynet_io(struct mbuf *m, int dir, struct ip_fw_args *fwa)
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
@@ -2015,7 +2070,7 @@ static void
ip_dn_init(void)
{
if (bootverbose)
- printf("DUMMYNET initialized (011031)\n");
+ printf("DUMMYNET with IPv6 initialized (040826)\n");
DUMMYNET_LOCK_INIT();
diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h
index d9c6aaa..947f081 100644
--- a/sys/netinet/ip_dummynet.h
+++ b/sys/netinet/ip_dummynet.h
@@ -124,10 +124,13 @@ struct dn_pkt_tag {
#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 */
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 0da6f43..5b196d6 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -137,6 +137,16 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
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,
/*
* actions for ng_ipfw
@@ -148,6 +158,16 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
};
/*
+ * 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,
@@ -291,6 +311,30 @@ typedef struct _ipfw_insn_log {
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.
*
@@ -354,8 +398,14 @@ struct ipfw_flow_id {
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.
*/
@@ -439,6 +489,21 @@ enum {
#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.
@@ -455,6 +520,8 @@ struct ip_fw_args {
struct ipfw_flow_id f_id; /* grabbed from IP header */
u_int32_t cookie; /* a cookie depending on rule action */
struct inpcb *inp;
+
+ struct _ip6dn_args dummypar; /* dummynet->ip6_output */
};
/*
diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c
index 563ac52..97f27ca 100644
--- a/sys/netinet/ip_fw2.c
+++ b/sys/netinet/ip_fw2.c
@@ -86,6 +86,9 @@
#include <netinet6/ipsec.h>
#endif
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
#include <netinet/if_ether.h> /* XXX for ETHERTYPE_IP */
#include <machine/in_cksum.h> /* XXX for in_cksum */
@@ -325,6 +328,7 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW,
#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)
@@ -555,6 +559,83 @@ verify_path(struct in_addr src, struct ifnet *ifp)
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...) */
@@ -773,7 +854,8 @@ hash_packet(struct ipfw_flow_id *id)
{
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;
}
@@ -912,19 +994,40 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction,
}
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:
@@ -1122,15 +1225,25 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
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;
@@ -1204,10 +1317,17 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
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)
@@ -1831,6 +1951,10 @@ ipfw_chk(struct ip_fw_args *args)
* 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 (IP_FW_PASS); /* accept */
@@ -1855,15 +1979,95 @@ do { \
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.
@@ -2027,11 +2231,13 @@ check_body:
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(
@@ -2086,7 +2292,7 @@ check_body:
break;
case O_FRAG:
- match = (hlen > 0 && offset != 0);
+ match = (offset != 0);
break;
case O_IN: /* "out" is "not in" */
@@ -2195,7 +2401,7 @@ check_body:
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)
@@ -2218,12 +2424,22 @@ check_body:
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:
@@ -2237,9 +2453,9 @@ check_body:
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;
@@ -2254,12 +2470,12 @@ check_body:
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:
@@ -2357,8 +2573,12 @@ check_body:
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;
@@ -2389,6 +2609,60 @@ check_body:
/* 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
@@ -3030,6 +3304,10 @@ check_ipfw_struct(struct ip_fw *rule, int size)
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;
@@ -3177,6 +3455,29 @@ check_action:
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);
@@ -3577,7 +3878,7 @@ ipfw_init(void)
}
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, "
diff --git a/sys/netinet/ip_fw_pfil.c b/sys/netinet/ip_fw_pfil.c
index 10a01ea..ab1ff55 100644
--- a/sys/netinet/ip_fw_pfil.c
+++ b/sys/netinet/ip_fw_pfil.c
@@ -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 */
@@ -155,7 +156,10 @@ again:
case IP_FW_DUMMYNET:
if (!DUMMYNET_LOADED)
goto drop;
- ip_dn_io_ptr(*m0, DN_TO_IP_IN, &args);
+ if (mtod(*m0, struct ip *)->ip_v == 4)
+ ip_dn_io_ptr(*m0, DN_TO_IP_IN, &args);
+ else if (mtod(*m0, struct ip *)->ip_v == 6)
+ ip_dn_io_ptr(*m0, DN_TO_IP6_IN, &args);
*m0 = NULL;
return 0; /* packet consumed */
@@ -278,7 +282,10 @@ again:
case IP_FW_DUMMYNET:
if (!DUMMYNET_LOADED)
break;
- ip_dn_io_ptr(*m0, DN_TO_IP_OUT, &args);
+ if (mtod(*m0, struct ip *)->ip_v == 4)
+ ip_dn_io_ptr(*m0, DN_TO_IP_OUT, &args);
+ else if (mtod(*m0, struct ip *)->ip_v == 6)
+ ip_dn_io_ptr(*m0, DN_TO_IP6_OUT, &args);
*m0 = NULL;
return 0; /* packet consumed */
@@ -410,6 +417,9 @@ static int
ipfw_hook(void)
{
struct pfil_head *pfh_inet;
+#ifdef INET6
+ struct pfil_head *pfh_inet6;
+#endif
if (ipfw_pfil_hooked)
return EEXIST;
@@ -417,9 +427,18 @@ ipfw_hook(void)
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;
}
@@ -428,6 +447,9 @@ static int
ipfw_unhook(void)
{
struct pfil_head *pfh_inet;
+#ifdef INET6
+ struct pfil_head *pfh_inet6;
+#endif
if (!ipfw_pfil_hooked)
return ENOENT;
@@ -435,9 +457,18 @@ ipfw_unhook(void)
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;
}
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 30f5959..36cfb28 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -78,6 +78,7 @@
#include <sys/kernel.h>
#include <net/if.h>
+#include <net/netisr.h>
#include <net/route.h>
#include <net/pfil.h>
@@ -167,6 +168,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
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;
@@ -524,6 +526,7 @@ skip_ipsec2:;
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
@@ -937,12 +940,35 @@ skip_ipsec2:;
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:
/*
* Send the packet to the outgoing interface.
OpenPOWER on IntegriCloud