* Add dynamic sysctl for net.inet6.ip6.fw.

* Correct handling of IPv6 Extension Headers.
* Add unreach6 code.
* Add logging for IPv6.

Submitted by:	sysctl handling derived from patch from ume needed for ip6fw
Obtained from:	is_icmp6_query and send_reject6 derived from similar
		functions of netinet6,ip6fw
Reviewed by:	ume, gnn; silence on ipfw@
Test setup provided by: CK Software GmbH
MFC after:	6 days
This commit is contained in:
Bjoern A. Zeeb 2005-08-13 11:02:34 +00:00
parent b37630e3a6
commit 9066356ba1
4 changed files with 377 additions and 70 deletions

View File

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 30, 2005
.Dd August 13, 2005
.Dt IPFW 8
.Os
.Sh NAME
@ -710,6 +710,10 @@ Synonym for
Discard packets that match this rule, and if the
packet is a TCP packet, try to send a TCP reset (RST) notice.
The search terminates.
.It Cm reset6
Discard packets that match this rule, and if the
packet is a TCP packet, try to send a TCP reset (RST) notice.
The search terminates.
.It Cm skipto Ar number
Skip all subsequent rules numbered less than
.Ar number .
@ -736,6 +740,17 @@ is a number from 0 to 255, or one of these aliases:
or
.Cm precedence-cutoff .
The search terminates.
.It Cm unreach6 Ar code
Discard packets that match this rule, and try to send an ICMPv6
unreachable notice with code
.Ar code ,
where
.Ar code
is a number from 0, 1, 3 or 4, or one of these aliases:
.Cm no-route, admin-prohib, address
or
.Cm port .
The search terminates.
.It Cm netgraph Ar cookie
Divert packet into netgraph with given
.Ar cookie .
@ -1036,6 +1051,8 @@ Hop-to-hop options
.Pq Cm hopopt ,
Source routing
.Pq Cm route ,
Destination options
.Pq Cm dstopt ,
IPSec authentication headers
.Pq Cm ah ,
and IPSec encapsulated security payload headers
@ -2018,6 +2035,8 @@ reinjected into the firewall at the next rule.
Enables verbose messages.
.It Em net.inet.ip.fw.verbose_limit : No 0
Limits the number of messages produced by a verbose firewall.
.It Em net.inet6.ip6.fw.deny_unknown_exthdrs : No 1
If enabled packets with unknown IPv6 Extension Headers will be denied.
.It Em net.link.ether.ipfw : No 0
Controls whether layer-2 packets are passed to
.Nm .

View File

@ -277,6 +277,8 @@ enum tokens {
TOK_SRCIP6,
TOK_IPV4,
TOK_UNREACH6,
TOK_RESET6,
};
struct _s_x dummynet_params[] = {
@ -326,7 +328,9 @@ struct _s_x rule_actions[] = {
{ "deny", TOK_DENY },
{ "drop", TOK_DENY },
{ "reject", TOK_REJECT },
{ "reset6", TOK_RESET6 },
{ "reset", TOK_RESET },
{ "unreach6", TOK_UNREACH6 },
{ "unreach", TOK_UNREACH },
{ "check-state", TOK_CHECKSTATE },
{ "//", TOK_COMMENT },
@ -851,6 +855,40 @@ print_reject_code(uint16_t code)
printf("unreach %u", code);
}
static struct _s_x icmp6codes[] = {
{ "no-route", ICMP6_DST_UNREACH_NOROUTE },
{ "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
{ "address", ICMP6_DST_UNREACH_ADDR },
{ "port", ICMP6_DST_UNREACH_NOPORT },
{ NULL, 0 }
};
static void
fill_unreach6_code(u_short *codep, char *str)
{
int val;
char *s;
val = strtoul(str, &s, 0);
if (s == str || *s != '\0' || val >= 0x100)
val = match_token(icmp6codes, str);
if (val < 0)
errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
*codep = val;
return;
}
static void
print_unreach6_code(uint16_t code)
{
char const *s = match_value(icmp6codes, code);
if (s != NULL)
printf("unreach6 %s", s);
else
printf("unreach6 %u", code);
}
/*
* Returns the number of bits set (from left) in a contiguous bitmask,
* or -1 if the mask is not contiguous.
@ -1169,6 +1207,7 @@ static struct _s_x ext6hdrcodes[] = {
{ "frag", EXT_FRAGMENT },
{ "hopopt", EXT_HOPOPTS },
{ "route", EXT_ROUTING },
{ "dstopt", EXT_DSTOPTS },
{ "ah", EXT_AH },
{ "esp", EXT_ESP },
{ NULL, 0 }
@ -1199,6 +1238,10 @@ fill_ext6hdr( ipfw_insn *cmd, char *av)
cmd->arg1 |= EXT_ROUTING;
break;
case EXT_DSTOPTS:
cmd->arg1 |= EXT_DSTOPTS;
break;
case EXT_AH:
cmd->arg1 |= EXT_AH;
break;
@ -1237,6 +1280,10 @@ print_ext6hdr( ipfw_insn *cmd )
printf("%crouting options", sep);
sep = ',';
}
if (cmd->arg1 & EXT_DSTOPTS ) {
printf("%cdestination options", sep);
sep = ',';
}
if (cmd->arg1 & EXT_AH ) {
printf("%cauthentication header", sep);
sep = ',';
@ -1406,6 +1453,13 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
print_reject_code(cmd->arg1);
break;
case O_UNREACH6:
if (cmd->arg1 == ICMP6_UNREACH_RST)
printf("reset6");
else
print_unreach6_code(cmd->arg1);
break;
case O_SKIPTO:
printf("skipto %u", cmd->arg1);
break;
@ -2495,8 +2549,9 @@ help(void)
"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 | unreach CODE | skipto N |\n"
" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n"
" skipto N | {divert|tee} PORT | forward ADDR |\n"
" pipe N | queue N\n"
"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
"ADDR: [ MAC dst src ether_type ] \n"
" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
@ -3754,6 +3809,11 @@ add(int ac, char *av[])
action->arg1 = ICMP_REJECT_RST;
break;
case TOK_RESET6:
action->opcode = O_UNREACH6;
action->arg1 = ICMP6_UNREACH_RST;
break;
case TOK_UNREACH:
action->opcode = O_REJECT;
NEED1("missing reject code");
@ -3761,6 +3821,13 @@ add(int ac, char *av[])
ac--; av++;
break;
case TOK_UNREACH6:
action->opcode = O_UNREACH6;
NEED1("missing unreach code");
fill_unreach6_code(&action->arg1, *av);
ac--; av++;
break;
case TOK_COUNT:
action->opcode = O_COUNT;
break;

View File

@ -155,6 +155,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_IP4,
O_UNREACH6, /* arg1=icmpv6 code arg (deny) */
O_LAST_OPCODE /* not an opcode! */
};
@ -167,6 +169,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
#define EXT_ROUTING 0x4
#define EXT_AH 0x8
#define EXT_ESP 0x10
#define EXT_DSTOPTS 0x20
/*
* Template for instructions.
@ -403,6 +406,7 @@ struct ipfw_flow_id {
struct in6_addr dst_ip6; /* could also store MAC addr! */
struct in6_addr src_ip6;
u_int32_t flow_id6;
u_int32_t frag_id6;
};
#define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6)
@ -451,6 +455,7 @@ struct _ipfw_dyn_rule {
#define IP_FW_TCPOPT_CC 0x10
#define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */
#define ICMP6_UNREACH_RST 0x100 /* fake ICMPv6 code (send a TCP RST) */
/*
* These are used for lookup tables.

View File

@ -34,6 +34,7 @@
#if !defined(KLD_MODULE)
#include "opt_ipfw.h"
#include "opt_ip6fw.h"
#include "opt_ipdn.h"
#include "opt_inet.h"
#include "opt_inet6.h"
@ -322,6 +323,15 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW,
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW,
&dyn_keepalive, 0, "Enable keepalives for dyn. rules");
/*
* IPv6 specific variables
*/
SYSCTL_DECL(_net_inet6_ip6);
static struct sysctl_ctx_list ip6_fw_sysctl_ctx;
static struct sysctl_oid *ip6_fw_sysctl_tree;
static int fw_deny_unknown_exthdrs = 1;
#endif /* SYSCTL_NODE */
@ -662,7 +672,88 @@ hash_packet6(struct ipfw_flow_id *id)
(id->dst_port) ^ (id->src_port) ^ (id->flow_id6);
return i;
}
/* end of ipv6 opcodes */
static int
is_icmp6_query(int icmp6_type)
{
if ((icmp6_type <= ICMP6_MAXTYPE) &&
(icmp6_type == ICMP6_ECHO_REQUEST ||
icmp6_type == ICMP6_MEMBERSHIP_QUERY ||
icmp6_type == ICMP6_WRUREQUEST ||
icmp6_type == ICMP6_FQDN_QUERY ||
icmp6_type == ICMP6_NI_QUERY))
return (1);
return (0);
}
static void
send_reject6(struct ip_fw_args *args, int code, u_short offset, u_int hlen)
{
if (code == ICMP6_UNREACH_RST && offset == 0 &&
args->f_id.proto == IPPROTO_TCP) {
struct ip6_hdr *ip6;
struct tcphdr *tcp;
tcp_seq ack, seq;
int flags;
struct {
struct ip6_hdr ip6;
struct tcphdr th;
} ti;
if (args->m->m_len < (hlen+sizeof(struct tcphdr))) {
args->m = m_pullup(args->m, hlen+sizeof(struct tcphdr));
if (args->m == NULL)
return;
}
ip6 = mtod(args->m, struct ip6_hdr *);
tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen);
if ((tcp->th_flags & TH_RST) != 0) {
m_freem(args->m);
return;
}
ti.ip6 = *ip6;
ti.th = *tcp;
ti.th.th_seq = ntohl(ti.th.th_seq);
ti.th.th_ack = ntohl(ti.th.th_ack);
ti.ip6.ip6_nxt = IPPROTO_TCP;
if (ti.th.th_flags & TH_ACK) {
ack = 0;
seq = ti.th.th_ack;
flags = TH_RST;
} else {
ack = ti.th.th_seq;
if (((args->m)->m_flags & M_PKTHDR) != 0) {
ack += (args->m)->m_pkthdr.len - hlen
- (ti.th.th_off << 2);
} else if (ip6->ip6_plen) {
ack += ntohs(ip6->ip6_plen) + sizeof(*ip6)
- hlen - (ti.th.th_off << 2);
} else {
m_freem(args->m);
return;
}
if (tcp->th_flags & TH_SYN)
ack++;
seq = 0;
flags = TH_RST|TH_ACK;
}
bcopy(&ti, ip6, sizeof(ti));
tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1),
args->m, ack, seq, flags);
} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */
icmp6_error(args->m, ICMP6_DST_UNREACH, code, 0);
} else
m_freem(args->m);
args->m = NULL;
}
#endif /* INET6 */
@ -676,12 +767,13 @@ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */
* XXX this function alone takes about 2Kbytes of code!
*/
static void
ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
struct mbuf *m, struct ifnet *oif)
ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset)
{
struct ether_header *eh = args->eh;
char *action;
int limit_reached = 0;
char action2[40], proto[48], fragment[28];
char action2[40], proto[128], fragment[32];
fragment[0] = '\0';
proto[0] = '\0';
@ -729,6 +821,14 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
cmd->arg1);
break;
case O_UNREACH6:
if (cmd->arg1==ICMP6_UNREACH_RST)
action = "Reset";
else
snprintf(SNPARGS(action2, 0), "Unreach %d",
cmd->arg1);
break;
case O_ACCEPT:
action = "Accept";
break;
@ -782,78 +882,123 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
if (hlen == 0) { /* non-ip */
snprintf(SNPARGS(proto, 0), "MAC");
} else {
struct ip *ip = mtod(m, struct ip *);
/* these three are all aliases to the same thing */
struct icmphdr *const icmp = L3HDR(struct icmphdr, ip);
struct tcphdr *const tcp = (struct tcphdr *)icmp;
struct udphdr *const udp = (struct udphdr *)icmp;
int ip_off, offset, ip_len;
int len;
char src[48], dst[48];
struct icmphdr *icmp;
struct tcphdr *tcp;
struct udphdr *udp;
/* Initialize to make compiler happy. */
struct ip *ip = NULL;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
struct icmp6_hdr *icmp6;
#endif
src[0] = '\0';
dst[0] = '\0';
#ifdef INET6
if (args->f_id.addr_type == 6) {
snprintf(src, sizeof(src), "[%s]",
ip6_sprintf(&args->f_id.src_ip6));
snprintf(dst, sizeof(dst), "[%s]",
ip6_sprintf(&args->f_id.dst_ip6));
if (eh != NULL) { /* layer 2 packets are as on the wire */
ip_off = ntohs(ip->ip_off);
ip_len = ntohs(ip->ip_len);
} else {
ip_off = ip->ip_off;
ip_len = ip->ip_len;
ip6 = (struct ip6_hdr *)mtod(m, struct ip6_hdr *);
tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen);
udp = (struct udphdr *)(mtod(args->m, char *) + hlen);
} else
#endif
{
ip = mtod(m, struct ip *);
tcp = L3HDR(struct tcphdr, ip);
udp = L3HDR(struct udphdr, ip);
inet_ntoa_r(ip->ip_src, src);
inet_ntoa_r(ip->ip_dst, dst);
}
offset = ip_off & IP_OFFMASK;
switch (ip->ip_p) {
switch (args->f_id.proto) {
case IPPROTO_TCP:
len = snprintf(SNPARGS(proto, 0), "TCP %s",
inet_ntoa(ip->ip_src));
len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d %s:%d",
ntohs(tcp->th_sport),
inet_ntoa(ip->ip_dst),
dst,
ntohs(tcp->th_dport));
else
snprintf(SNPARGS(proto, len), " %s",
inet_ntoa(ip->ip_dst));
snprintf(SNPARGS(proto, len), " %s", dst);
break;
case IPPROTO_UDP:
len = snprintf(SNPARGS(proto, 0), "UDP %s",
inet_ntoa(ip->ip_src));
len = snprintf(SNPARGS(proto, 0), "UDP %s", src);
if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d %s:%d",
ntohs(udp->uh_sport),
inet_ntoa(ip->ip_dst),
dst,
ntohs(udp->uh_dport));
else
snprintf(SNPARGS(proto, len), " %s",
inet_ntoa(ip->ip_dst));
snprintf(SNPARGS(proto, len), " %s", dst);
break;
case IPPROTO_ICMP:
icmp = L3HDR(struct icmphdr, ip);
if (offset == 0)
len = snprintf(SNPARGS(proto, 0),
"ICMP:%u.%u ",
icmp->icmp_type, icmp->icmp_code);
else
len = snprintf(SNPARGS(proto, 0), "ICMP ");
len += snprintf(SNPARGS(proto, len), "%s",
inet_ntoa(ip->ip_src));
snprintf(SNPARGS(proto, len), " %s",
inet_ntoa(ip->ip_dst));
len += snprintf(SNPARGS(proto, len), "%s", src);
snprintf(SNPARGS(proto, len), " %s", dst);
break;
#ifdef INET6
case IPPROTO_ICMPV6:
icmp6 = (struct icmp6_hdr *)(mtod(args->m, char *) + hlen);
if (offset == 0)
len = snprintf(SNPARGS(proto, 0),
"ICMPv6:%u.%u ",
icmp6->icmp6_type, icmp6->icmp6_code);
else
len = snprintf(SNPARGS(proto, 0), "ICMPv6 ");
len += snprintf(SNPARGS(proto, len), "%s", src);
snprintf(SNPARGS(proto, len), " %s", dst);
break;
#endif
default:
len = snprintf(SNPARGS(proto, 0), "P:%d %s", ip->ip_p,
inet_ntoa(ip->ip_src));
snprintf(SNPARGS(proto, len), " %s",
inet_ntoa(ip->ip_dst));
len = snprintf(SNPARGS(proto, 0), "P:%d %s",
args->f_id.proto, src);
snprintf(SNPARGS(proto, len), " %s", dst);
break;
}
if (ip_off & (IP_MF | IP_OFFMASK))
snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)",
ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2),
offset << 3,
(ip_off & IP_MF) ? "+" : "");
#ifdef INET6
if (args->f_id.addr_type == 6) {
if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
snprintf(SNPARGS(fragment, 0),
" (frag %08x:%d@%d%s)",
args->f_id.frag_id6,
ntohs(ip6->ip6_plen) - hlen,
ntohs(offset & IP6F_OFF_MASK) << 3,
(offset & IP6F_MORE_FRAG) ? "+" : "");
} else
#endif
{
int ip_off, ip_len;
if (eh != NULL) { /* layer 2 packets are as on the wire */
ip_off = ntohs(ip->ip_off);
ip_len = ntohs(ip->ip_len);
} else {
ip_off = ip->ip_off;
ip_len = ip->ip_len;
}
if (ip_off & (IP_MF | IP_OFFMASK))
snprintf(SNPARGS(fragment, 0),
" (frag %d:%d@%d%s)",
ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2),
offset << 3,
(ip_off & IP_MF) ? "+" : "");
}
}
if (oif || m->m_pkthdr.rcvif)
log(LOG_SECURITY | LOG_INFO,
@ -1483,7 +1628,7 @@ send_pkt(struct ipfw_flow_id *id, u_int32_t seq, u_int32_t ack, int flags)
* sends a reject message, consuming the mbuf passed as an argument.
*/
static void
send_reject(struct ip_fw_args *args, int code, int offset, int ip_len)
send_reject(struct ip_fw_args *args, int code, u_short offset, int ip_len)
{
if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */
@ -1947,6 +2092,11 @@ ipfw_chk(struct ip_fw_args *args)
* we have a fragment at this offset of an IPv4 packet.
* offset == 0 means that (if this is an IPv4 packet)
* this is the first or only fragment.
* For IPv6 offset == 0 means there is no Fragment Header.
* If offset != 0 for IPv6 always use correct mask to
* get the correct offset because we add IP6F_MORE_FRAG
* to be able to dectect the first fragment which would
* otherwise have offset = 0.
*/
u_short offset = 0;
@ -1998,6 +2148,7 @@ ipfw_chk(struct ip_fw_args *args)
pktlen = m->m_pkthdr.len;
proto = args->f_id.proto = 0; /* mark f_id invalid */
/* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */
/*
* PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous,
@ -2046,54 +2197,92 @@ do { \
src_port = UDP(ulp)->uh_sport;
break;
case IPPROTO_HOPOPTS:
case IPPROTO_HOPOPTS: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_hbh);
ext_hd |= EXT_HOPOPTS;
hlen += sizeof(struct ip6_hbh);
hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3;
proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
ulp = NULL;
break;
case IPPROTO_ROUTING:
case IPPROTO_ROUTING: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_rthdr);
if (((struct ip6_rthdr *)ulp)->ip6r_type != 0) {
printf("IPFW2: IPV6 - Unknown Routing "
"Header type(%d)\n",
((struct ip6_rthdr *)ulp)->ip6r_type);
if (fw_deny_unknown_exthdrs)
return (IP_FW_DENY);
break;
}
ext_hd |= EXT_ROUTING;
hlen += sizeof(struct ip6_rthdr);
hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3;
proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt;
ulp = NULL;
break;
case IPPROTO_FRAGMENT:
case IPPROTO_FRAGMENT: /* RFC 2460 */
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 ? */
offset = ((struct ip6_frag *)ulp)->ip6f_offlg &
IP6F_OFF_MASK;
/* Add IP6F_MORE_FRAG for offset of first
* fragment to be != 0. */
offset |= ((struct ip6_frag *)ulp)->ip6f_offlg &
IP6F_MORE_FRAG;
if (offset == 0) {
printf("IPFW2: IPV6 - Invalid Fragment "
"Header\n");
if (fw_deny_unknown_exthdrs)
return (IP_FW_DENY);
break;
}
args->f_id.frag_id6 =
ntohl(((struct ip6_frag *)ulp)->ip6f_ident);
ulp = NULL;
break;
case IPPROTO_AH:
case IPPROTO_NONE:
case IPPROTO_ESP:
case IPPROTO_DSTOPTS: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_hbh);
ext_hd |= EXT_DSTOPTS;
hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3;
proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
ulp = NULL;
break;
case IPPROTO_AH: /* RFC 2402 */
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);
hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2;
proto = ((struct ip6_ext *)ulp)->ip6e_nxt;
ulp = NULL;
break;
case IPPROTO_ESP: /* RFC 2406 */
PULLUP_TO(hlen, ulp, uint32_t); /* SPI, Seq# */
/* Anything past Seq# is variable length and
* data past this ext. header is encrypted. */
ext_hd |= EXT_ESP;
break;
case IPPROTO_NONE: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_ext);
/* Packet ends here. if ip6e_len!=0 octets
* must be ignored. */
break;
case IPPROTO_OSPFIGP:
/* XXX OSPF header check? */
PULLUP_TO(hlen, ulp, struct ip6_ext);
break;
default:
printf( "IPFW2: IPV6 - Unknown Extension Header (%d)\n",
proto);
return 0; /* deny */
printf("IPFW2: IPV6 - Unknown Extension "
"Header(%d), ext_hd=%x\n", proto, ext_hd);
if (fw_deny_unknown_exthdrs)
return (IP_FW_DENY);
break;
} /*switch */
}
@ -2101,7 +2290,7 @@ do { \
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);
args->f_id.flow_id6 = ntohl(mtod(m, struct ip6_hdr *)->ip6_flow);
} else if (pktlen >= sizeof(struct ip) &&
(args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) &&
mtod(m, struct ip *)->ip_v == 4) {
@ -2604,8 +2793,8 @@ do { \
}
case O_LOG:
if (fw_verbose && !is_ipv6)
ipfw_log(f, hlen, args->eh, m, oif);
if (fw_verbose)
ipfw_log(f, hlen, args, m, oif, offset);
match = 1;
break;
@ -2873,7 +3062,6 @@ do { \
* if the packet is not ICMP (or is an ICMP
* query), and it is not multicast/broadcast.
*/
/* XXX: IPv6 missing!?! */
if (hlen > 0 && is_ipv4 &&
(proto != IPPROTO_ICMP ||
is_icmp_query(ICMP(ulp))) &&
@ -2884,6 +3072,19 @@ do { \
m = args->m;
}
/* FALLTHROUGH */
#ifdef INET6
case O_UNREACH6:
if (hlen > 0 && is_ipv6 &&
(proto != IPPROTO_ICMPV6 ||
(is_icmp6_query(args->f_id.flags) == 1)) &&
!(m->m_flags & (M_BCAST|M_MCAST)) &&
!IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) {
send_reject6(args, cmd->arg1,
offset, hlen);
m = args->m;
}
/* FALLTHROUGH */
#endif
case O_DENY:
retval = IP_FW_DENY;
goto done;
@ -3504,6 +3705,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_ACCEPT:
case O_DENY:
case O_REJECT:
case O_UNREACH6:
case O_SKIPTO:
check_size:
if (cmdlen != F_INSN_SIZE(ipfw_insn))
@ -3931,6 +4133,16 @@ ipfw_init(void)
struct ip_fw default_rule;
int error;
/* Setup IPv6 fw sysctl tree. */
sysctl_ctx_init(&ip6_fw_sysctl_ctx);
ip6_fw_sysctl_tree = SYSCTL_ADD_NODE(&ip6_fw_sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_net_inet6_ip6), OID_AUTO, "fw",
CTLFLAG_RW | CTLFLAG_SECURE, 0, "Firewall");
SYSCTL_ADD_INT(&ip6_fw_sysctl_ctx, SYSCTL_CHILDREN(ip6_fw_sysctl_tree),
OID_AUTO, "deny_unknown_exthdrs", CTLFLAG_RW | CTLFLAG_SECURE,
&fw_deny_unknown_exthdrs, 0,
"Deny packets with unknown IPv6 Extension Headers");
layer3_chain.rules = NULL;
layer3_chain.want_write = 0;
layer3_chain.busy_count = 0;
@ -4022,5 +4234,9 @@ ipfw_destroy(void)
IPFW_DYN_LOCK_DESTROY();
uma_zdestroy(ipfw_dyn_rule_zone);
IPFW_LOCK_DESTROY(&layer3_chain);
/* Free IPv6 fw sysctl tree. */
sysctl_ctx_free(&ip6_fw_sysctl_ctx);
printf("IP firewall unloaded\n");
}