static int can_handle(const struct sk_buff *skb)
{
- if(!ip_hdr(skb)) /* not IP */
+ struct iphdr iphdr_tmp;
+ struct iphdr *iphdr;
+ int offset;
+
+ if (!ip_hdr(skb))
return 0;
- if(ip_hdr(skb)->protocol != IPPROTO_TCP &&
- ip_hdr(skb)->protocol != IPPROTO_UDP &&
- ip_hdr(skb)->protocol != IPPROTO_ICMP)
+
+ offset = ((uintptr_t)ip_hdr(skb)) - ((uintptr_t)skb->data);
+
+ iphdr = skb_header_pointer(skb, offset, sizeof(*iphdr), &iphdr_tmp);
+ if (!iphdr)
return 0;
- return 1;
+
+ if (iphdr->protocol == IPPROTO_TCP ||
+ iphdr->protocol == IPPROTO_UDP ||
+ iphdr->protocol == IPPROTO_ICMP)
+ return 1;
+
+ return 0;
}
-/* Returns offset the into the skb->data that the application data starts */
static int app_data_offset(const struct sk_buff *skb)
{
- /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
- isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
- int ip_hl = 4*ip_hdr(skb)->ihl;
-
- if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
- /* 12 == offset into TCP header for the header length field.
- Can't get this with skb->h.th->doff because the tcphdr
- struct doesn't get set when routing (this is confirmed to be
- true in Netfilter as well as QoS.) */
- int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
-
- return ip_hl + tcp_hl;
- } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) {
- return ip_hl + 8; /* UDP header is always 8 bytes */
- } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
- return ip_hl + 8; /* ICMP header is 8 bytes */
- } else {
- if (net_ratelimit())
- printk(KERN_ERR "layer7: tried to handle unknown "
- "protocol!\n");
- return ip_hl + 8; /* something reasonable */
+ int offset;
+ struct iphdr iphdr_tmp;
+ struct iphdr *iphdr;
+ struct tcphdr tcphdr_tmp;
+ struct tcphdr *tcphdr;
+
+ if (!ip_hdr(skb))
+ return -1;
+
+ offset = ((uintptr_t)ip_hdr(skb)) - ((uintptr_t)skb->data);
+
+ iphdr = skb_header_pointer(skb, offset, sizeof(*iphdr), &iphdr_tmp);
+ if (!iphdr)
+ return -1;
+
+ offset += iphdr->ihl * 4;
+
+ if (iphdr->protocol == IPPROTO_TCP) {
+ tcphdr = skb_header_pointer(skb, offset, sizeof(*tcphdr),
+ &tcphdr_tmp);
+ if (!tcphdr)
+ return -1;
+
+ offset += tcphdr->doff * 4;
+
+ return offset;
}
+
+ if (iphdr->protocol == IPPROTO_UDP)
+ return offset + 8;
+
+ if (iphdr->protocol == IPPROTO_ICMP)
+ return offset + 8;
+
+ if (net_ratelimit())
+ pr_err(KERN_ERR "layer7: tried to handle unknown protocol!\n");
+
+ return offset + 8; /* something reasonable */
}
/* handles whether there's a match when we aren't appending data anymore */
return length;
}
+/* add the new app data to the buffer. Return number of bytes added. */
+static int add_data(char *target, int offset, const struct sk_buff *skb)
+{
+ int length, length_sum = 0;
+ int data_start = app_data_offset(skb);
+ int remaining = skb->len - data_start;
+ int to_copy;
+ uint8_t buf[512];
+ uint8_t *data;
+
+ while ((offset < maxdatalen - 1) && (remaining > 0)) {
+ to_copy = min_t(int, remaining, sizeof(buf));
+
+ data = skb_header_pointer(skb, data_start, to_copy, buf);
+ length = add_datastr(target, offset, data, to_copy);
+
+ remaining -= to_copy;
+ data_start += to_copy;
+ offset += length;
+ length_sum += length;
+ }
+
+ return length_sum;
+}
+
/* add the new app data to the conntrack. Return number of bytes added. */
-static int add_data(struct nf_conn * master_conntrack,
- char * app_data, int appdatalen)
+static int add_data_conntrack(struct nf_conn *master_conntrack,
+ const struct sk_buff *skb)
{
int length;
- length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen);
+ length = add_data(master_conntrack->layer7.app_data,
+ master_conntrack->layer7.app_data_len, skb);
master_conntrack->layer7.app_data_len += length;
return length;
static bool match(const struct sk_buff *skbin, struct xt_action_param *par)
{
/* sidestep const without getting a compiler warning... */
- struct sk_buff * skb = (struct sk_buff *)skbin;
+ struct sk_buff *skb = (struct sk_buff *)skbin;
const struct xt_layer7_info * info = par->matchinfo;
enum ip_conntrack_info master_ctinfo, ctinfo;
struct nf_conn *master_conntrack, *conntrack;
- unsigned char *app_data, *tmp_data;
- unsigned int pattern_result, appdatalen;
+ unsigned char *tmp_data;
+ unsigned int pattern_result;
regexp * comppattern;
/* Be paranoid/incompetent - lock the entire match function. */
spin_lock_bh(&l7_lock);
- if(!can_handle(skb)){
+ if (!can_handle(skbin)) {
DPRINTK("layer7: This is some protocol I can't handle.\n");
spin_unlock_bh(&l7_lock);
return info->invert;
/* Treat parent & all its children together as one connection, except
for the purpose of setting conntrack->layer7.app_proto in the actual
connection. This makes /proc/net/ip_conntrack more satisfying. */
- if(!(conntrack = nf_ct_get(skb, &ctinfo)) ||
- !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){
+ conntrack = nf_ct_get(skbin, &ctinfo);
+ master_conntrack = nf_ct_get(skbin, &master_ctinfo);
+ if (!conntrack || !master_conntrack) {
DPRINTK("layer7: couldn't get conntrack.\n");
spin_unlock_bh(&l7_lock);
return info->invert;
return (pattern_result ^ info->invert);
}
- if(skb_is_nonlinear(skb)){
- if(skb_linearize(skb) != 0){
- if (net_ratelimit())
- printk(KERN_ERR "layer7: failed to linearize "
- "packet, bailing.\n");
- spin_unlock_bh(&l7_lock);
- return info->invert;
- }
- }
-
- /* now that the skb is linearized, it's safe to set these. */
- app_data = skb->data + app_data_offset(skb);
- appdatalen = skb_tail_pointer(skb) - app_data;
-
/* the return value gets checked later, when we're ready to use it */
comppattern = compile_and_cache(info->pattern, info->protocol);
}
tmp_data[0] = '\0';
- add_datastr(tmp_data, 0, app_data, appdatalen);
+ add_data(tmp_data, 0, skbin);
pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0);
kfree(tmp_data);
if(!skb->cb[0]){
int newbytes;
- newbytes = add_data(master_conntrack, app_data, appdatalen);
+ newbytes = add_data_conntrack(master_conntrack, skb);
if(newbytes == 0) { /* didn't add any data */
skb->cb[0] = 1;