Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw2.c
Show First 20 Lines • Show All 228 Lines • ▼ Show 20 Lines | |||||
* Other macros just cast void * into the appropriate type | * 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 TCP(p) ((struct tcphdr *)(p)) | ||||
#define SCTP(p) ((struct sctphdr *)(p)) | #define SCTP(p) ((struct sctphdr *)(p)) | ||||
#define UDP(p) ((struct udphdr *)(p)) | #define UDP(p) ((struct udphdr *)(p)) | ||||
#define ICMP(p) ((struct icmphdr *)(p)) | #define ICMP(p) ((struct icmphdr *)(p)) | ||||
#define ICMP6(p) ((struct icmp6_hdr *)(p)) | #define ICMP6(p) ((struct icmp6_hdr *)(p)) | ||||
#define IP6F(p) ((struct ip6_frag *)(p)) | |||||
static __inline int | static __inline int | ||||
icmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd) | icmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd) | ||||
{ | { | ||||
int type = icmp->icmp_type; | int type = icmp->icmp_type; | ||||
return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<<type)) ); | return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<<type)) ); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 243 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* support for IP6_*_ME opcodes */ | /* support for IP6_*_ME opcodes */ | ||||
static const struct in6_addr lla_mask = {{{ | static const struct in6_addr lla_mask = {{{ | ||||
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, | 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, | ||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | ||||
}}}; | }}}; | ||||
static int | int | ||||
ipfw_localip6(struct in6_addr *in6) | ipfw_localip6(struct in6_addr *in6) | ||||
{ | { | ||||
struct rm_priotracker in6_ifa_tracker; | struct rm_priotracker in6_ifa_tracker; | ||||
struct in6_ifaddr *ia; | struct in6_ifaddr *ia; | ||||
if (IN6_IS_ADDR_MULTICAST(in6)) | if (IN6_IS_ADDR_MULTICAST(in6)) | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
icmp6_error(m, ICMP6_DST_UNREACH, code, 0); | icmp6_error(m, ICMP6_DST_UNREACH, code, 0); | ||||
} else | } else | ||||
FREE_PKT(m); | FREE_PKT(m); | ||||
args->m = NULL; | args->m = NULL; | ||||
} | } | ||||
/* | |||||
* Simplified version of ip6_lasthdr(). | |||||
* This function assumes that mbuf is contiguous enough to read the last | |||||
* header without need to make m_pullup(). Also, it assumes that ipfw_chk() | |||||
* already did this work, so it is known that we have valid packet and there | |||||
* is no need to do boundary checks again. | |||||
* | |||||
* Returns protocol number and optionally offset of the last header. | |||||
*/ | |||||
int | |||||
ipfw_ip6lasthdr(struct mbuf *m, int *offset) | |||||
{ | |||||
struct ip6_hdr *ip6; | |||||
struct ip6_hbh *hbh; | |||||
int proto, hlen; | |||||
ip6 = mtod(m, struct ip6_hdr *); | |||||
hlen = sizeof(*ip6); | |||||
proto = ip6->ip6_nxt; | |||||
while (proto == IPPROTO_HOPOPTS || proto == IPPROTO_ROUTING || | |||||
proto == IPPROTO_DSTOPTS) { | |||||
hbh = mtodo(m, hlen); | |||||
proto = hbh->ip6h_nxt; | |||||
hlen += hbh->ip6h_len << 3; | |||||
} | |||||
if (offset != NULL) | |||||
*offset = hlen; | |||||
return (proto); | |||||
} | |||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
/* | /* | ||||
* 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 iplen, struct ip *ip) | send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip) | ||||
▲ Show 20 Lines • Show All 215 Lines • ▼ Show 20 Lines | jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num, | ||||
if (jump_backwards == 0 && num <= f->rulenum) | if (jump_backwards == 0 && num <= f->rulenum) | ||||
num = f->rulenum + 1; | num = f->rulenum + 1; | ||||
f_pos = chain->idxmap[num]; | f_pos = chain->idxmap[num]; | ||||
return (f_pos); | return (f_pos); | ||||
} | } | ||||
#endif | #endif | ||||
/* | |||||
* Do IP fragments reassembly. | |||||
* Return: | |||||
* 0 - reassembly is not needed. | |||||
* 1 - reassembly is completed | |||||
*/ | |||||
int | |||||
ipfw_reass(struct mbuf **mp, int addr_type, int offset) | |||||
{ | |||||
struct ip *ip; | |||||
int hlen; | |||||
switch (addr_type) { | |||||
case 4: | |||||
ip = mtod(*mp, struct ip *); | |||||
if ((ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) == 0) | |||||
return (0); | |||||
*mp = ip_reass(*mp); | |||||
if (*mp != NULL) { | |||||
/* IP header checksum fixup after reassembly */ | |||||
ip = mtod(*mp, struct ip *); | |||||
hlen = ip->ip_hl << 2; | |||||
ip->ip_sum = 0; | |||||
ip->ip_sum = hlen > sizeof(*ip) ? in_cksum_hdr(ip): | |||||
in_cksum(*mp, hlen); | |||||
} | |||||
break; | |||||
#ifdef INET6 | |||||
case 6: | |||||
if (offset == 0 && | |||||
ipfw_ip6lasthdr(*mp, &offset) != IPPROTO_FRAGMENT) | |||||
return (0); | |||||
*mp = ip6_reass(*mp, &offset, &hlen /* unused */, 1); | |||||
break; | |||||
#endif /* INET6 */ | |||||
default: | |||||
return (0); | |||||
} | |||||
return (1); | |||||
} | |||||
#define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) | #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) | ||||
/* | /* | ||||
* The main check routine for the firewall. | * The main check routine for the firewall. | ||||
* | * | ||||
* All arguments are in args so we can modify them and return them | * All arguments are in args so we can modify them and return them | ||||
* back to the caller. | * back to the caller. | ||||
* | * | ||||
* Parameters: | * Parameters: | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | #endif | ||||
* 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|ip6f_mf == 0 means there is no Fragment Header | * For IPv6 offset|ip6f_mf == 0 means there is no Fragment Header | ||||
* or there is a single packet fragment (fragment header added | * or there is a single packet fragment (fragment header added | ||||
* without needed). We will treat a single packet fragment as if | * without needed). We will treat a single packet fragment as if | ||||
* there was no fragment header (or log/block depending on the | * there was no fragment header (or log/block depending on the | ||||
* V_fw_permit_single_frag6 sysctl setting). | * V_fw_permit_single_frag6 sysctl setting). | ||||
* | |||||
* ip6fh_off The offset of an ip6_frag header. | |||||
*/ | */ | ||||
u_short offset = 0; | u_short offset = 0; | ||||
u_short ip6f_mf = 0; | u_short ip6f_mf = 0; | ||||
u_short ip6fh_off = 0; | |||||
/* | /* | ||||
* Local copies of addresses. They are only valid if we have | * Local copies of addresses. They are only valid if we have | ||||
* an IP packet. | * an IP packet. | ||||
* | * | ||||
* proto The protocol. Set to 0 for non-ip packets, | * proto The protocol. Set to 0 for non-ip packets, | ||||
* or to the protocol read from the packet otherwise. | * or to the protocol read from the packet otherwise. | ||||
* proto != 0 means that we have an IPv4 packet. | * proto != 0 means that we have an IPv4 packet. | ||||
▲ Show 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | while (ulp == NULL && offset == 0) { | ||||
hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; | 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: /* RFC 2460 */ | 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; | ||||
ip6fh_off = hlen; | |||||
hlen += sizeof (struct ip6_frag); | hlen += sizeof (struct ip6_frag); | ||||
proto = ((struct ip6_frag *)ulp)->ip6f_nxt; | proto = IP6F(ulp)->ip6f_nxt; | ||||
offset = ((struct ip6_frag *)ulp)->ip6f_offlg & | offset = IP6F(ulp)->ip6f_offlg & IP6F_OFF_MASK; | ||||
IP6F_OFF_MASK; | ip6f_mf = IP6F(ulp)->ip6f_offlg & | ||||
ip6f_mf = ((struct ip6_frag *)ulp)->ip6f_offlg & | |||||
IP6F_MORE_FRAG; | IP6F_MORE_FRAG; | ||||
if (V_fw_permit_single_frag6 == 0 && | if (V_fw_permit_single_frag6 == 0 && | ||||
offset == 0 && ip6f_mf == 0) { | offset == 0 && ip6f_mf == 0) { | ||||
if (V_fw_verbose) | if (V_fw_verbose) | ||||
printf("IPFW2: IPV6 - Invalid " | printf("IPFW2: IPV6 - Invalid " | ||||
"Fragment Header\n"); | "Fragment Header\n"); | ||||
if (V_fw_deny_unknown_exthdrs) | if (V_fw_deny_unknown_exthdrs) | ||||
return (IP_FW_DENY); | return (IP_FW_DENY); | ||||
break; | break; | ||||
} | } | ||||
args->f_id.extra = | args->f_id.extra = | ||||
ntohl(((struct ip6_frag *)ulp)->ip6f_ident); | ntohl(IP6F(ulp)->ip6f_ident); | ||||
ulp = NULL; | ulp = NULL; | ||||
break; | break; | ||||
case IPPROTO_DSTOPTS: /* RFC 2460 */ | case IPPROTO_DSTOPTS: /* RFC 2460 */ | ||||
PULLUP_TO(hlen, ulp, struct ip6_hbh); | PULLUP_TO(hlen, ulp, struct ip6_hbh); | ||||
ext_hd |= EXT_DSTOPTS; | ext_hd |= EXT_DSTOPTS; | ||||
hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; | hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; | ||||
proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; | proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | if (pktlen >= sizeof(struct ip6_hdr) && | ||||
*/ | */ | ||||
proto = ip->ip_p; | proto = ip->ip_p; | ||||
src_ip = ip->ip_src; | src_ip = ip->ip_src; | ||||
dst_ip = ip->ip_dst; | dst_ip = ip->ip_dst; | ||||
offset = ntohs(ip->ip_off) & IP_OFFMASK; | offset = ntohs(ip->ip_off) & IP_OFFMASK; | ||||
iplen = ntohs(ip->ip_len); | iplen = ntohs(ip->ip_len); | ||||
pktlen = iplen < pktlen ? iplen : pktlen; | pktlen = iplen < pktlen ? iplen : pktlen; | ||||
/* | |||||
* XXX: what to do with first fragment, that has | |||||
* not enough length, but it is still valid? | |||||
*/ | |||||
if (offset == 0) { | if (offset == 0) { | ||||
switch (proto) { | switch (proto) { | ||||
case IPPROTO_TCP: | case IPPROTO_TCP: | ||||
PULLUP_TO(hlen, ulp, struct tcphdr); | PULLUP_TO(hlen, ulp, struct tcphdr); | ||||
dst_port = TCP(ulp)->th_dport; | dst_port = TCP(ulp)->th_dport; | ||||
src_port = TCP(ulp)->th_sport; | src_port = TCP(ulp)->th_sport; | ||||
/* save flags for dynamic rules */ | /* save flags for dynamic rules */ | ||||
args->f_id._flags = TCP(ulp)->th_flags; | args->f_id._flags = TCP(ulp)->th_flags; | ||||
▲ Show 20 Lines • Show All 1,342 Lines • ▼ Show 20 Lines | #endif | ||||
break; | break; | ||||
} | } | ||||
if (cmd->arg1 != IP_FW_TARG) | if (cmd->arg1 != IP_FW_TARG) | ||||
((ipfw_insn_nat *)cmd)->nat = t; | ((ipfw_insn_nat *)cmd)->nat = t; | ||||
} | } | ||||
retval = ipfw_nat_ptr(args, t, m); | retval = ipfw_nat_ptr(args, t, m); | ||||
break; | break; | ||||
case O_REASS: { | case O_REASS: | ||||
int ip_off; | |||||
IPFW_INC_RULE_COUNTER(f, pktlen); | IPFW_INC_RULE_COUNTER(f, pktlen); | ||||
l = 0; /* in any case exit inner loop */ | l = 0; /* in any case exit inner loop */ | ||||
ip_off = ntohs(ip->ip_off); | if (ipfw_reass(&args->m, | ||||
args->f_id.addr_type, ip6fh_off) == 0) | |||||
/* if not fragmented, go to next rule */ | |||||
if ((ip_off & (IP_MF | IP_OFFMASK)) == 0) | |||||
break; | break; | ||||
if (args->m == NULL) | |||||
args->m = m = ip_reass(m); | |||||
/* | |||||
* do IP header checksum fixup. | |||||
*/ | |||||
if (m == NULL) { /* fragment got swallowed */ | |||||
retval = IP_FW_DENY; | retval = IP_FW_DENY; | ||||
} else { /* good, packet complete */ | else { | ||||
int hlen; | |||||
ip = mtod(m, struct ip *); | |||||
hlen = ip->ip_hl << 2; | |||||
ip->ip_sum = 0; | |||||
if (hlen == sizeof(struct ip)) | |||||
ip->ip_sum = in_cksum_hdr(ip); | |||||
else | |||||
ip->ip_sum = in_cksum(m, hlen); | |||||
retval = IP_FW_REASS; | retval = IP_FW_REASS; | ||||
set_match(args, f_pos, chain); | set_match(args, f_pos, chain); | ||||
} | } | ||||
done = 1; /* exit outer loop */ | done = 1; /* exit outer loop */ | ||||
break; | break; | ||||
} | |||||
case O_EXTERNAL_ACTION: | case O_EXTERNAL_ACTION: | ||||
l = 0; /* in any case exit inner loop */ | l = 0; /* in any case exit inner loop */ | ||||
retval = ipfw_run_eaction(chain, args, | retval = ipfw_run_eaction(chain, args, | ||||
cmd, &done); | cmd, &done); | ||||
/* | /* | ||||
* If both @retval and @done are zero, | * If both @retval and @done are zero, | ||||
* consider this as rule matching and | * consider this as rule matching and | ||||
* update counters. | * update counters. | ||||
▲ Show 20 Lines • Show All 386 Lines • Show Last 20 Lines |