* 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
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=149020
4 changed files with 377 additions and 70 deletions

View File

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

View File

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

View File

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

View File

@ -34,6 +34,7 @@
#if !defined(KLD_MODULE) #if !defined(KLD_MODULE)
#include "opt_ipfw.h" #include "opt_ipfw.h"
#include "opt_ip6fw.h"
#include "opt_ipdn.h" #include "opt_ipdn.h"
#include "opt_inet.h" #include "opt_inet.h"
#include "opt_inet6.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, SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW,
&dyn_keepalive, 0, "Enable keepalives for dyn. rules"); &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 */ #endif /* SYSCTL_NODE */
@ -662,7 +672,88 @@ hash_packet6(struct ipfw_flow_id *id)
(id->dst_port) ^ (id->src_port) ^ (id->flow_id6); (id->dst_port) ^ (id->src_port) ^ (id->flow_id6);
return i; 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 */ #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! * XXX this function alone takes about 2Kbytes of code!
*/ */
static void static void
ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh, ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif) struct mbuf *m, struct ifnet *oif, u_short offset)
{ {
struct ether_header *eh = args->eh;
char *action; char *action;
int limit_reached = 0; int limit_reached = 0;
char action2[40], proto[48], fragment[28]; char action2[40], proto[128], fragment[32];
fragment[0] = '\0'; fragment[0] = '\0';
proto[0] = '\0'; proto[0] = '\0';
@ -729,6 +821,14 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
cmd->arg1); cmd->arg1);
break; 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: case O_ACCEPT:
action = "Accept"; action = "Accept";
break; break;
@ -782,78 +882,123 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
if (hlen == 0) { /* non-ip */ if (hlen == 0) { /* non-ip */
snprintf(SNPARGS(proto, 0), "MAC"); snprintf(SNPARGS(proto, 0), "MAC");
} else { } 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; 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 */ ip6 = (struct ip6_hdr *)mtod(m, struct ip6_hdr *);
ip_off = ntohs(ip->ip_off); tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen);
ip_len = ntohs(ip->ip_len); udp = (struct udphdr *)(mtod(args->m, char *) + hlen);
} else { } else
ip_off = ip->ip_off; #endif
ip_len = ip->ip_len; {
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: case IPPROTO_TCP:
len = snprintf(SNPARGS(proto, 0), "TCP %s", len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
inet_ntoa(ip->ip_src));
if (offset == 0) if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d %s:%d", snprintf(SNPARGS(proto, len), ":%d %s:%d",
ntohs(tcp->th_sport), ntohs(tcp->th_sport),
inet_ntoa(ip->ip_dst), dst,
ntohs(tcp->th_dport)); ntohs(tcp->th_dport));
else else
snprintf(SNPARGS(proto, len), " %s", snprintf(SNPARGS(proto, len), " %s", dst);
inet_ntoa(ip->ip_dst));
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
len = snprintf(SNPARGS(proto, 0), "UDP %s", len = snprintf(SNPARGS(proto, 0), "UDP %s", src);
inet_ntoa(ip->ip_src));
if (offset == 0) if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d %s:%d", snprintf(SNPARGS(proto, len), ":%d %s:%d",
ntohs(udp->uh_sport), ntohs(udp->uh_sport),
inet_ntoa(ip->ip_dst), dst,
ntohs(udp->uh_dport)); ntohs(udp->uh_dport));
else else
snprintf(SNPARGS(proto, len), " %s", snprintf(SNPARGS(proto, len), " %s", dst);
inet_ntoa(ip->ip_dst));
break; break;
case IPPROTO_ICMP: case IPPROTO_ICMP:
icmp = L3HDR(struct icmphdr, ip);
if (offset == 0) if (offset == 0)
len = snprintf(SNPARGS(proto, 0), len = snprintf(SNPARGS(proto, 0),
"ICMP:%u.%u ", "ICMP:%u.%u ",
icmp->icmp_type, icmp->icmp_code); icmp->icmp_type, icmp->icmp_code);
else else
len = snprintf(SNPARGS(proto, 0), "ICMP "); len = snprintf(SNPARGS(proto, 0), "ICMP ");
len += snprintf(SNPARGS(proto, len), "%s", len += snprintf(SNPARGS(proto, len), "%s", src);
inet_ntoa(ip->ip_src)); snprintf(SNPARGS(proto, len), " %s", dst);
snprintf(SNPARGS(proto, len), " %s",
inet_ntoa(ip->ip_dst));
break; 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: default:
len = snprintf(SNPARGS(proto, 0), "P:%d %s", ip->ip_p, len = snprintf(SNPARGS(proto, 0), "P:%d %s",
inet_ntoa(ip->ip_src)); args->f_id.proto, src);
snprintf(SNPARGS(proto, len), " %s", snprintf(SNPARGS(proto, len), " %s", dst);
inet_ntoa(ip->ip_dst));
break; break;
} }
if (ip_off & (IP_MF | IP_OFFMASK)) #ifdef INET6
snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)", if (args->f_id.addr_type == 6) {
ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2), if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
offset << 3, snprintf(SNPARGS(fragment, 0),
(ip_off & IP_MF) ? "+" : ""); " (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) if (oif || m->m_pkthdr.rcvif)
log(LOG_SECURITY | LOG_INFO, 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. * sends a reject message, consuming the mbuf passed as an argument.
*/ */
static void 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 */ 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. * we have a fragment at this offset of an IPv4 packet.
* offset == 0 means that (if this is an IPv4 packet) * offset == 0 means that (if this is an IPv4 packet)
* this is the first or only fragment. * 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; u_short offset = 0;
@ -1998,6 +2148,7 @@ ipfw_chk(struct ip_fw_args *args)
pktlen = m->m_pkthdr.len; pktlen = m->m_pkthdr.len;
proto = args->f_id.proto = 0; /* mark f_id invalid */ 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, * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous,
@ -2046,54 +2197,92 @@ do { \
src_port = UDP(ulp)->uh_sport; src_port = UDP(ulp)->uh_sport;
break; break;
case IPPROTO_HOPOPTS: case IPPROTO_HOPOPTS: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_hbh); PULLUP_TO(hlen, ulp, struct ip6_hbh);
ext_hd |= EXT_HOPOPTS; 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; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
ulp = NULL; ulp = NULL;
break; break;
case IPPROTO_ROUTING: case IPPROTO_ROUTING: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_rthdr); 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; 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; proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt;
ulp = NULL; ulp = NULL;
break; break;
case IPPROTO_FRAGMENT: case IPPROTO_FRAGMENT: /* RFC 2460 */
PULLUP_TO(hlen, ulp, struct ip6_frag); PULLUP_TO(hlen, ulp, struct ip6_frag);
ext_hd |= EXT_FRAGMENT; ext_hd |= EXT_FRAGMENT;
hlen += sizeof (struct ip6_frag); hlen += sizeof (struct ip6_frag);
proto = ((struct ip6_frag *)ulp)->ip6f_nxt; proto = ((struct ip6_frag *)ulp)->ip6f_nxt;
offset = 1; offset = ((struct ip6_frag *)ulp)->ip6f_offlg &
ulp = NULL; /* XXX is it correct ? */ 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; break;
case IPPROTO_AH: case IPPROTO_DSTOPTS: /* RFC 2460 */
case IPPROTO_NONE: PULLUP_TO(hlen, ulp, struct ip6_hbh);
case IPPROTO_ESP: 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); PULLUP_TO(hlen, ulp, struct ip6_ext);
if (proto == IPPROTO_AH)
ext_hd |= EXT_AH; ext_hd |= EXT_AH;
else if (proto == IPPROTO_ESP) hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2;
ext_hd |= EXT_ESP;
hlen += ((struct ip6_ext *)ulp)->ip6e_len +
sizeof (struct ip6_ext);
proto = ((struct ip6_ext *)ulp)->ip6e_nxt; proto = ((struct ip6_ext *)ulp)->ip6e_nxt;
ulp = NULL; ulp = NULL;
break; 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: case IPPROTO_OSPFIGP:
/* XXX OSPF header check? */ /* XXX OSPF header check? */
PULLUP_TO(hlen, ulp, struct ip6_ext); PULLUP_TO(hlen, ulp, struct ip6_ext);
break; break;
default: default:
printf( "IPFW2: IPV6 - Unknown Extension Header (%d)\n", printf("IPFW2: IPV6 - Unknown Extension "
proto); "Header(%d), ext_hd=%x\n", proto, ext_hd);
return 0; /* deny */ if (fw_deny_unknown_exthdrs)
return (IP_FW_DENY);
break; break;
} /*switch */ } /*switch */
} }
@ -2101,7 +2290,7 @@ do { \
args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst; args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst;
args->f_id.src_ip = 0; args->f_id.src_ip = 0;
args->f_id.dst_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) && } else if (pktlen >= sizeof(struct ip) &&
(args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) &&
mtod(m, struct ip *)->ip_v == 4) { mtod(m, struct ip *)->ip_v == 4) {
@ -2604,8 +2793,8 @@ do { \
} }
case O_LOG: case O_LOG:
if (fw_verbose && !is_ipv6) if (fw_verbose)
ipfw_log(f, hlen, args->eh, m, oif); ipfw_log(f, hlen, args, m, oif, offset);
match = 1; match = 1;
break; break;
@ -2873,7 +3062,6 @@ do { \
* if the packet is not ICMP (or is an ICMP * if the packet is not ICMP (or is an ICMP
* query), and it is not multicast/broadcast. * query), and it is not multicast/broadcast.
*/ */
/* XXX: IPv6 missing!?! */
if (hlen > 0 && is_ipv4 && if (hlen > 0 && is_ipv4 &&
(proto != IPPROTO_ICMP || (proto != IPPROTO_ICMP ||
is_icmp_query(ICMP(ulp))) && is_icmp_query(ICMP(ulp))) &&
@ -2884,6 +3072,19 @@ do { \
m = args->m; m = args->m;
} }
/* FALLTHROUGH */ /* 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: case O_DENY:
retval = IP_FW_DENY; retval = IP_FW_DENY;
goto done; goto done;
@ -3504,6 +3705,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_ACCEPT: case O_ACCEPT:
case O_DENY: case O_DENY:
case O_REJECT: case O_REJECT:
case O_UNREACH6:
case O_SKIPTO: case O_SKIPTO:
check_size: check_size:
if (cmdlen != F_INSN_SIZE(ipfw_insn)) if (cmdlen != F_INSN_SIZE(ipfw_insn))
@ -3931,6 +4133,16 @@ ipfw_init(void)
struct ip_fw default_rule; struct ip_fw default_rule;
int error; 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.rules = NULL;
layer3_chain.want_write = 0; layer3_chain.want_write = 0;
layer3_chain.busy_count = 0; layer3_chain.busy_count = 0;
@ -4022,5 +4234,9 @@ ipfw_destroy(void)
IPFW_DYN_LOCK_DESTROY(); IPFW_DYN_LOCK_DESTROY();
uma_zdestroy(ipfw_dyn_rule_zone); uma_zdestroy(ipfw_dyn_rule_zone);
IPFW_LOCK_DESTROY(&layer3_chain); IPFW_LOCK_DESTROY(&layer3_chain);
/* Free IPv6 fw sysctl tree. */
sysctl_ctx_free(&ip6_fw_sysctl_ctx);
printf("IP firewall unloaded\n"); printf("IP firewall unloaded\n");
} }