Commit 8dcfc9e3 authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Linearize iptables matches.

Adjusts the IPTables matches to handle non-linear packets.
Untested: ipt_ah and ipt_esp.
parent 4b5fb65e
...@@ -35,14 +35,16 @@ match(const struct sk_buff *skb, ...@@ -35,14 +35,16 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ahhdr *ah = hdr; struct ahhdr ah;
const struct ipt_ah *ahinfo = matchinfo; const struct ipt_ah *ahinfo = matchinfo;
if (offset == 0 && datalen < sizeof(struct ahhdr)) { /* Must not be a fragment. */
if (offset)
return 0;
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &ah, sizeof(ah)) < 0) {
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("Dropping evil AH tinygram.\n"); duprintf("Dropping evil AH tinygram.\n");
...@@ -50,11 +52,9 @@ match(const struct sk_buff *skb, ...@@ -50,11 +52,9 @@ match(const struct sk_buff *skb,
return 0; return 0;
} }
/* Must not be a fragment. */ return spi_match(ahinfo->spis[0], ahinfo->spis[1],
return !offset ntohl(ah.spi),
&& spi_match(ahinfo->spis[0], ahinfo->spis[1], !!(ahinfo->invflags & IPT_AH_INV_SPI));
ntohl(ah->spi),
!!(ahinfo->invflags & IPT_AH_INV_SPI));
} }
/* Called when user tries to insert an entry of this type. */ /* Called when user tries to insert an entry of this type. */
......
...@@ -14,8 +14,6 @@ match(const struct sk_buff *skb, ...@@ -14,8 +14,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_conntrack_info *sinfo = matchinfo; const struct ipt_conntrack_info *sinfo = matchinfo;
......
...@@ -19,8 +19,7 @@ MODULE_LICENSE("GPL"); ...@@ -19,8 +19,7 @@ MODULE_LICENSE("GPL");
static int match(const struct sk_buff *skb, const struct net_device *in, static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo, const struct net_device *out, const void *matchinfo,
int offset, const void *hdr, u_int16_t datalen, int offset, int *hotdrop)
int *hotdrop)
{ {
const struct ipt_dscp_info *info = matchinfo; const struct ipt_dscp_info *info = matchinfo;
const struct iphdr *iph = skb->nh.iph; const struct iphdr *iph = skb->nh.iph;
......
...@@ -19,34 +19,40 @@ MODULE_DESCRIPTION("IP tables ECN matching module"); ...@@ -19,34 +19,40 @@ MODULE_DESCRIPTION("IP tables ECN matching module");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static inline int match_ip(const struct sk_buff *skb, static inline int match_ip(const struct sk_buff *skb,
const struct iphdr *iph,
const struct ipt_ecn_info *einfo) const struct ipt_ecn_info *einfo)
{ {
return ((iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect); return ((skb->nh.iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect);
} }
static inline int match_tcp(const struct sk_buff *skb, static inline int match_tcp(const struct sk_buff *skb,
const struct iphdr *iph, const struct ipt_ecn_info *einfo,
const struct ipt_ecn_info *einfo) int *hotdrop)
{ {
struct tcphdr *tcph = (void *)iph + iph->ihl*4; struct tcphdr tcph;
/* In practice, TCP match does this, so can't fail. But let's
be good citizens. */
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) {
*hotdrop = 0;
return 0;
}
if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { if (einfo->invert & IPT_ECN_OP_MATCH_ECE) {
if (tcph->ece == 1) if (tcph.ece == 1)
return 0; return 0;
} else { } else {
if (tcph->ece == 0) if (tcph.ece == 0)
return 0; return 0;
} }
} }
if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { if (einfo->invert & IPT_ECN_OP_MATCH_CWR) {
if (tcph->cwr == 1) if (tcph.cwr == 1)
return 0; return 0;
} else { } else {
if (tcph->cwr == 0) if (tcph.cwr == 0)
return 0; return 0;
} }
} }
...@@ -56,20 +62,18 @@ static inline int match_tcp(const struct sk_buff *skb, ...@@ -56,20 +62,18 @@ static inline int match_tcp(const struct sk_buff *skb,
static int match(const struct sk_buff *skb, const struct net_device *in, static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo, const struct net_device *out, const void *matchinfo,
int offset, const void *hdr, u_int16_t datalen, int offset, int *hotdrop)
int *hotdrop)
{ {
const struct ipt_ecn_info *info = matchinfo; const struct ipt_ecn_info *info = matchinfo;
const struct iphdr *iph = skb->nh.iph;
if (info->operation & IPT_ECN_OP_MATCH_IP) if (info->operation & IPT_ECN_OP_MATCH_IP)
if (!match_ip(skb, iph, info)) if (!match_ip(skb, info))
return 0; return 0;
if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) {
if (iph->protocol != IPPROTO_TCP) if (skb->nh.iph->protocol != IPPROTO_TCP)
return 0; return 0;
if (!match_tcp(skb, iph, info)) if (!match_tcp(skb, info, hotdrop))
return 0; return 0;
} }
......
...@@ -35,14 +35,16 @@ match(const struct sk_buff *skb, ...@@ -35,14 +35,16 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct esphdr *esp = hdr; struct esphdr esp;
const struct ipt_esp *espinfo = matchinfo; const struct ipt_esp *espinfo = matchinfo;
if (offset == 0 && datalen < sizeof(struct esphdr)) { /* Must not be a fragment. */
if (offset)
return 0;
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &esp, sizeof(esp)) < 0) {
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("Dropping evil ESP tinygram.\n"); duprintf("Dropping evil ESP tinygram.\n");
...@@ -50,11 +52,9 @@ match(const struct sk_buff *skb, ...@@ -50,11 +52,9 @@ match(const struct sk_buff *skb,
return 0; return 0;
} }
/* Must not be a fragment. */ return spi_match(espinfo->spis[0], espinfo->spis[1],
return !offset ntohl(esp.spi),
&& spi_match(espinfo->spis[0], espinfo->spis[1], !!(espinfo->invflags & IPT_ESP_INV_SPI));
ntohl(esp->spi),
!!(espinfo->invflags & IPT_ESP_INV_SPI));
} }
/* Called when user tries to insert an entry of this type. */ /* Called when user tries to insert an entry of this type. */
......
...@@ -28,8 +28,6 @@ match(const struct sk_buff *skb, ...@@ -28,8 +28,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_helper_info *info = matchinfo; const struct ipt_helper_info *info = matchinfo;
......
...@@ -15,8 +15,6 @@ match(const struct sk_buff *skb, ...@@ -15,8 +15,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_length_info *info = matchinfo; const struct ipt_length_info *info = matchinfo;
......
...@@ -47,8 +47,6 @@ ipt_limit_match(const struct sk_buff *skb, ...@@ -47,8 +47,6 @@ ipt_limit_match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
struct ipt_rateinfo *r = ((struct ipt_rateinfo *)matchinfo)->master; struct ipt_rateinfo *r = ((struct ipt_rateinfo *)matchinfo)->master;
......
...@@ -12,8 +12,6 @@ match(const struct sk_buff *skb, ...@@ -12,8 +12,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_mac_info *info = matchinfo; const struct ipt_mac_info *info = matchinfo;
......
...@@ -11,8 +11,6 @@ match(const struct sk_buff *skb, ...@@ -11,8 +11,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_mark_info *info = matchinfo; const struct ipt_mark_info *info = matchinfo;
......
...@@ -39,15 +39,18 @@ match(const struct sk_buff *skb, ...@@ -39,15 +39,18 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct udphdr *udp = hdr; u16 ports[2];
const struct ipt_multiport *multiinfo = matchinfo; const struct ipt_multiport *multiinfo = matchinfo;
/* Must be big enough to read ports. */ /* Must not be a fragment. */
if (offset == 0 && datalen < sizeof(struct udphdr)) { if (offset)
return 0;
/* Must be big enough to read ports (both UDP and TCP have
them at the start). */
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, ports, sizeof(ports)) < 0) {
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("ipt_multiport:" duprintf("ipt_multiport:"
...@@ -56,11 +59,9 @@ match(const struct sk_buff *skb, ...@@ -56,11 +59,9 @@ match(const struct sk_buff *skb,
return 0; return 0;
} }
/* Must not be a fragment. */ return ports_match(multiinfo->ports,
return !offset multiinfo->flags, multiinfo->count,
&& ports_match(multiinfo->ports, ntohs(ports[0]), ntohs(ports[1]));
multiinfo->flags, multiinfo->count,
ntohs(udp->source), ntohs(udp->dest));
} }
/* Called when user tries to insert an entry of this type. */ /* Called when user tries to insert an entry of this type. */
......
...@@ -115,8 +115,6 @@ match(const struct sk_buff *skb, ...@@ -115,8 +115,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_owner_info *info = matchinfo; const struct ipt_owner_info *info = matchinfo;
...@@ -170,8 +168,11 @@ checkentry(const char *tablename, ...@@ -170,8 +168,11 @@ checkentry(const char *tablename,
return 0; return 0;
} }
if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info))) if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info))) {
printk("Matchsize %u != %u\n", matchsize,
IPT_ALIGN(sizeof(struct ipt_owner_info)));
return 0; return 0;
}
return 1; return 1;
} }
......
...@@ -13,8 +13,6 @@ static int match(const struct sk_buff *skb, ...@@ -13,8 +13,6 @@ static int match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_pkttype_info *info = matchinfo; const struct ipt_pkttype_info *info = matchinfo;
......
...@@ -13,8 +13,6 @@ match(const struct sk_buff *skb, ...@@ -13,8 +13,6 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_state_info *sinfo = matchinfo; const struct ipt_state_info *sinfo = matchinfo;
......
...@@ -11,24 +11,32 @@ ...@@ -11,24 +11,32 @@
/* Returns 1 if the mss option is set and matched by the range, 0 otherwise */ /* Returns 1 if the mss option is set and matched by the range, 0 otherwise */
static inline int static inline int
mssoption_match(u_int16_t min, u_int16_t max, mssoption_match(u_int16_t min, u_int16_t max,
const struct tcphdr *tcp, const struct sk_buff *skb,
u_int16_t datalen,
int invert, int invert,
int *hotdrop) int *hotdrop)
{ {
unsigned int i; struct tcphdr tcph;
const u_int8_t *opt = (u_int8_t *)tcp; /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
u8 opt[15 * 4 - sizeof(tcph)];
unsigned int i, optlen;
/* If we don't have the whole header, drop packet. */ /* If we don't have the whole header, drop packet. */
if (tcp->doff * 4 > datalen) { if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0)
*hotdrop = 1; goto dropit;
return 0;
} /* Malformed. */
if (tcph.doff*4 < sizeof(tcph))
for (i = sizeof(struct tcphdr); i < tcp->doff * 4; ) { goto dropit;
if ((opt[i] == TCPOPT_MSS)
&& ((tcp->doff * 4 - i) >= TCPOLEN_MSS) optlen = tcph.doff*4 - sizeof(tcph);
&& (opt[i+1] == TCPOLEN_MSS)) { /* Truncated options. */
if (skb_copy_bits(skb, skb->nh.iph->ihl*4+sizeof(tcph), opt, optlen)<0)
goto dropit;
for (i = 0; i < optlen; ) {
if (opt[i] == TCPOPT_MSS
&& (optlen - i) >= TCPOLEN_MSS
&& opt[i+1] == TCPOLEN_MSS) {
u_int16_t mssval; u_int16_t mssval;
mssval = (opt[i+2] << 8) | opt[i+3]; mssval = (opt[i+2] << 8) | opt[i+3];
...@@ -38,8 +46,11 @@ mssoption_match(u_int16_t min, u_int16_t max, ...@@ -38,8 +46,11 @@ mssoption_match(u_int16_t min, u_int16_t max,
if (opt[i] < 2) i++; if (opt[i] < 2) i++;
else i += opt[i+1]?:1; else i += opt[i+1]?:1;
} }
return invert; return invert;
dropit:
*hotdrop = 1;
return 0;
} }
static int static int
...@@ -48,15 +59,11 @@ match(const struct sk_buff *skb, ...@@ -48,15 +59,11 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_tcpmss_match_info *info = matchinfo; const struct ipt_tcpmss_match_info *info = matchinfo;
const struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
return mssoption_match(info->mss_min, info->mss_max, tcph, return mssoption_match(info->mss_min, info->mss_max, skb,
skb->len - skb->nh.iph->ihl*4,
info->invert, hotdrop); info->invert, hotdrop);
} }
......
...@@ -11,14 +11,11 @@ match(const struct sk_buff *skb, ...@@ -11,14 +11,11 @@ match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct ipt_tos_info *info = matchinfo; const struct ipt_tos_info *info = matchinfo;
const struct iphdr *iph = skb->nh.iph;
return (iph->tos == info->tos) ^ info->invert; return (skb->nh.iph->tos == info->tos) ^ info->invert;
} }
static int static int
......
...@@ -19,24 +19,22 @@ MODULE_LICENSE("GPL"); ...@@ -19,24 +19,22 @@ MODULE_LICENSE("GPL");
static int match(const struct sk_buff *skb, const struct net_device *in, static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo, const struct net_device *out, const void *matchinfo,
int offset, const void *hdr, u_int16_t datalen, int offset, int *hotdrop)
int *hotdrop)
{ {
const struct ipt_ttl_info *info = matchinfo; const struct ipt_ttl_info *info = matchinfo;
const struct iphdr *iph = skb->nh.iph;
switch (info->mode) { switch (info->mode) {
case IPT_TTL_EQ: case IPT_TTL_EQ:
return (iph->ttl == info->ttl); return (skb->nh.iph->ttl == info->ttl);
break; break;
case IPT_TTL_NE: case IPT_TTL_NE:
return (!(iph->ttl == info->ttl)); return (!(skb->nh.iph->ttl == info->ttl));
break; break;
case IPT_TTL_LT: case IPT_TTL_LT:
return (iph->ttl < info->ttl); return (skb->nh.iph->ttl < info->ttl);
break; break;
case IPT_TTL_GT: case IPT_TTL_GT:
return (iph->ttl > info->ttl); return (skb->nh.iph->ttl > info->ttl);
break; break;
default: default:
printk(KERN_WARNING "ipt_ttl: unknown mode %d\n", printk(KERN_WARNING "ipt_ttl: unknown mode %d\n",
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment