URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/ipv6/netfilter
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/ip6t_eui64.c
0,0 → 1,89
/* Kernel module to match EUI64 address parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/if_ether.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
|
unsigned char eui64[8]; |
int i=0; |
|
if ( !(skb->mac.raw >= skb->head |
&& (skb->mac.raw + ETH_HLEN) <= skb->data) |
&& offset != 0) { |
*hotdrop = 1; |
return 0; |
} |
|
memset(eui64, 0, sizeof(eui64)); |
|
if (skb->mac.ethernet->h_proto == ntohs(ETH_P_IPV6)) { |
if (skb->nh.ipv6h->version == 0x6) { |
memcpy(eui64, skb->mac.ethernet->h_source, 3); |
memcpy(eui64 + 5, skb->mac.ethernet->h_source + 3, 3); |
eui64[3]=0xff; |
eui64[4]=0xfe; |
eui64[0] |= 0x02; |
|
i=0; |
while ((skb->nh.ipv6h->saddr.in6_u.u6_addr8[8+i] == |
eui64[i]) && (i<8)) i++; |
|
if ( i == 8 ) |
return 1; |
} |
} |
|
return 0; |
} |
|
static int |
ip6t_eui64_checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (hook_mask |
& ~((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_IN) | |
(1 << NF_IP6_PRE_ROUTING) )) { |
printk("ip6t_eui64: only valid for PRE_ROUTING, LOCAL_IN or FORWARD.\n"); |
return 0; |
} |
|
if (matchsize != IP6T_ALIGN(sizeof(int))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match eui64_match |
= { { NULL, NULL }, "eui64", &match, &ip6t_eui64_checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&eui64_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&eui64_match); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_DESCRIPTION("IPv6 EUI64 address checking match"); |
MODULE_LICENSE("GPL"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
/ip6t_ipv6header.c
0,0 → 1,154
/* ipv6header match - matches IPv6 packets based |
on whether they contain certain headers */ |
|
/* Original idea: Brad Chapman |
* Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */ |
|
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_ipv6header.h> |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("IPv6 headers match"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
static int |
ipv6header_match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_ipv6header_info *info = matchinfo; |
unsigned int temp; |
int len; |
u8 nexthdr; |
unsigned int ptr; |
|
/* Make sure this isn't an evil packet */ |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
int hdrlen; |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
temp |= MASK_NONE; |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
temp |= MASK_ESP; |
break; |
} |
|
hdr=(struct ipv6_opt_hdr *)skb->data+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
temp |= MASK_HOPOPTS; |
break; |
case NEXTHDR_ROUTING: |
temp |= MASK_ROUTING; |
break; |
case NEXTHDR_FRAGMENT: |
temp |= MASK_FRAGMENT; |
break; |
case NEXTHDR_AUTH: |
temp |= MASK_AH; |
break; |
case NEXTHDR_DEST: |
temp |= MASK_DSTOPTS; |
break; |
default: |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
break; |
} |
} |
|
if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) ) |
temp |= MASK_PROTO; |
|
if (info->modeflag) |
return (!( (temp & info->matchflags) |
^ info->matchflags) ^ info->invflags); |
else |
return (!( temp ^ info->matchflags) ^ info->invflags); |
} |
|
static int |
ipv6header_checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
/* Check for obvious errors */ |
/* This match is valid in all hooks! */ |
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))) { |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match |
ip6t_ipv6header_match = { |
{ NULL, NULL }, |
"ipv6header", |
&ipv6header_match, |
&ipv6header_checkentry, |
NULL, |
THIS_MODULE |
}; |
|
static int __init ipv6header_init(void) |
{ |
return ip6t_register_match(&ip6t_ipv6header_match); |
} |
|
static void __exit ipv6header_exit(void) |
{ |
ip6t_unregister_match(&ip6t_ipv6header_match); |
} |
|
module_init(ipv6header_init); |
module_exit(ipv6header_exit); |
|
/ip6t_multiport.c
0,0 → 1,101
/* Kernel module to match one of a list of TCP/UDP ports: ports are in |
the same place so we can treat them as equal. */ |
#include <linux/module.h> |
#include <linux/types.h> |
#include <linux/udp.h> |
#include <linux/skbuff.h> |
#include <linux/in.h> |
|
#include <linux/netfilter_ipv6/ip6t_multiport.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
#if 0 |
#define duprintf(format, args...) printk(format , ## args) |
#else |
#define duprintf(format, args...) |
#endif |
|
/* Returns 1 if the port is matched by the test, 0 otherwise. */ |
static inline int |
ports_match(const u_int16_t *portlist, enum ip6t_multiport_flags flags, |
u_int8_t count, u_int16_t src, u_int16_t dst) |
{ |
unsigned int i; |
for (i=0; i<count; i++) { |
if (flags != IP6T_MULTIPORT_DESTINATION |
&& portlist[i] == src) |
return 1; |
|
if (flags != IP6T_MULTIPORT_SOURCE |
&& portlist[i] == dst) |
return 1; |
} |
|
return 0; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct udphdr *udp = hdr; |
const struct ip6t_multiport *multiinfo = matchinfo; |
|
/* Must be big enough to read ports. */ |
if (offset == 0 && datalen < sizeof(struct udphdr)) { |
/* We've been asked to examine this packet, and we |
can't. Hence, no choice but to drop. */ |
duprintf("ip6t_multiport:" |
" Dropping evil offset=0 tinygram.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
/* Must not be a fragment. */ |
return !offset |
&& ports_match(multiinfo->ports, |
multiinfo->flags, multiinfo->count, |
ntohs(udp->source), ntohs(udp->dest)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
const struct ip6t_multiport *multiinfo = matchinfo; |
|
/* Must specify proto == TCP/UDP, no unknown flags or bad count */ |
return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP) |
&& !(ip->flags & IP6T_INV_PROTO) |
&& matchsize == IP6T_ALIGN(sizeof(struct ip6t_multiport)) |
&& (multiinfo->flags == IP6T_MULTIPORT_SOURCE |
|| multiinfo->flags == IP6T_MULTIPORT_DESTINATION |
|| multiinfo->flags == IP6T_MULTIPORT_EITHER) |
&& multiinfo->count <= IP6T_MULTI_PORTS; |
} |
|
static struct ip6t_match multiport_match |
= { { NULL, NULL }, "multiport", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&multiport_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&multiport_match); |
} |
|
module_init(init); |
module_exit(fini); |
/ip6_queue.c
0,0 → 1,710
/* |
* This is a module which is used for queueing IPv6 packets and |
* communicating with userspace via netlink. |
* |
* (C) 2001 Fernando Anton, this code is GPL. |
* IPv64 Project - Work based in IPv64 draft by Arturo Azcorra. |
* Universidad Carlos III de Madrid - Leganes (Madrid) - Spain |
* Universidad Politecnica de Alcala de Henares - Alcala de H. (Madrid) - Spain |
* email: fanton@it.uc3m.es |
* |
* 2001-11-06: First try. Working with ip_queue.c for IPv4 and trying |
* to adapt it to IPv6 |
* HEAVILY based in ipqueue.c by James Morris. It's just |
* a little modified version of it, so he's nearly the |
* real coder of this. |
* Few changes needed, mainly the hard_routing code and |
* the netlink socket protocol (we're NETLINK_IP6_FW). |
* 2002-06-25: Code cleanup. [JM: ported cleanup over from ip_queue.c] |
*/ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/init.h> |
#include <linux/ipv6.h> |
#include <linux/notifier.h> |
#include <linux/netdevice.h> |
#include <linux/netfilter.h> |
#include <linux/netlink.h> |
#include <linux/spinlock.h> |
#include <linux/brlock.h> |
#include <linux/sysctl.h> |
#include <linux/proc_fs.h> |
#include <net/sock.h> |
#include <net/ipv6.h> |
#include <net/ip6_route.h> |
#include <linux/netfilter_ipv4/ip_queue.h> |
#include <linux/netfilter_ipv4/ip_tables.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
#define IPQ_QMAX_DEFAULT 1024 |
#define IPQ_PROC_FS_NAME "ip6_queue" |
#define NET_IPQ_QMAX 2088 |
#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" |
|
struct ipq_rt_info { |
struct in6_addr daddr; |
struct in6_addr saddr; |
}; |
|
struct ipq_queue_entry { |
struct list_head list; |
struct nf_info *info; |
struct sk_buff *skb; |
struct ipq_rt_info rt_info; |
}; |
|
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); |
|
static unsigned char copy_mode = IPQ_COPY_NONE; |
static unsigned int queue_maxlen = IPQ_QMAX_DEFAULT; |
static rwlock_t queue_lock = RW_LOCK_UNLOCKED; |
static int peer_pid; |
static unsigned int copy_range; |
static unsigned int queue_total; |
static struct sock *ipqnl; |
static LIST_HEAD(queue_list); |
static DECLARE_MUTEX(ipqnl_sem); |
|
static void |
ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict) |
{ |
nf_reinject(entry->skb, entry->info, verdict); |
kfree(entry); |
} |
|
static inline int |
__ipq_enqueue_entry(struct ipq_queue_entry *entry) |
{ |
if (queue_total >= queue_maxlen) { |
if (net_ratelimit()) |
printk(KERN_WARNING "ip6_queue: full at %d entries, " |
"dropping packet(s).\n", queue_total); |
return -ENOSPC; |
} |
list_add(&entry->list, &queue_list); |
queue_total++; |
return 0; |
} |
|
/* |
* Find and return a queued entry matched by cmpfn, or return the last |
* entry if cmpfn is NULL. |
*/ |
static inline struct ipq_queue_entry * |
__ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data) |
{ |
struct list_head *p; |
|
list_for_each_prev(p, &queue_list) { |
struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p; |
|
if (!cmpfn || cmpfn(entry, data)) |
return entry; |
} |
return NULL; |
} |
|
static inline void |
__ipq_dequeue_entry(struct ipq_queue_entry *entry) |
{ |
list_del(&entry->list); |
queue_total--; |
} |
|
static inline struct ipq_queue_entry * |
__ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) |
{ |
struct ipq_queue_entry *entry; |
|
entry = __ipq_find_entry(cmpfn, data); |
if (entry == NULL) |
return NULL; |
|
__ipq_dequeue_entry(entry); |
return entry; |
} |
|
|
static inline void |
__ipq_flush(int verdict) |
{ |
struct ipq_queue_entry *entry; |
|
while ((entry = __ipq_find_dequeue_entry(NULL, 0))) |
ipq_issue_verdict(entry, verdict); |
} |
|
static inline int |
__ipq_set_mode(unsigned char mode, unsigned int range) |
{ |
int status = 0; |
|
switch(mode) { |
case IPQ_COPY_NONE: |
case IPQ_COPY_META: |
copy_mode = mode; |
copy_range = 0; |
break; |
|
case IPQ_COPY_PACKET: |
copy_mode = mode; |
copy_range = range; |
if (copy_range > 0xFFFF) |
copy_range = 0xFFFF; |
break; |
|
default: |
status = -EINVAL; |
|
} |
return status; |
} |
|
static inline void |
__ipq_reset(void) |
{ |
peer_pid = 0; |
__ipq_set_mode(IPQ_COPY_NONE, 0); |
__ipq_flush(NF_DROP); |
} |
|
static struct ipq_queue_entry * |
ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) |
{ |
struct ipq_queue_entry *entry; |
|
write_lock_bh(&queue_lock); |
entry = __ipq_find_dequeue_entry(cmpfn, data); |
write_unlock_bh(&queue_lock); |
return entry; |
} |
|
static void |
ipq_flush(int verdict) |
{ |
write_lock_bh(&queue_lock); |
__ipq_flush(verdict); |
write_unlock_bh(&queue_lock); |
} |
|
static struct sk_buff * |
ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) |
{ |
unsigned char *old_tail; |
size_t size = 0; |
size_t data_len = 0; |
struct sk_buff *skb; |
struct ipq_packet_msg *pmsg; |
struct nlmsghdr *nlh; |
|
read_lock_bh(&queue_lock); |
|
switch (copy_mode) { |
case IPQ_COPY_META: |
case IPQ_COPY_NONE: |
size = NLMSG_SPACE(sizeof(*pmsg)); |
data_len = 0; |
break; |
|
case IPQ_COPY_PACKET: |
if (copy_range == 0 || copy_range > entry->skb->len) |
data_len = entry->skb->len; |
else |
data_len = copy_range; |
|
size = NLMSG_SPACE(sizeof(*pmsg) + data_len); |
break; |
|
default: |
*errp = -EINVAL; |
read_unlock_bh(&queue_lock); |
return NULL; |
} |
|
read_unlock_bh(&queue_lock); |
|
skb = alloc_skb(size, GFP_ATOMIC); |
if (!skb) |
goto nlmsg_failure; |
|
old_tail= skb->tail; |
nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); |
pmsg = NLMSG_DATA(nlh); |
memset(pmsg, 0, sizeof(*pmsg)); |
|
pmsg->packet_id = (unsigned long )entry; |
pmsg->data_len = data_len; |
pmsg->timestamp_sec = entry->skb->stamp.tv_sec; |
pmsg->timestamp_usec = entry->skb->stamp.tv_usec; |
pmsg->mark = entry->skb->nfmark; |
pmsg->hook = entry->info->hook; |
pmsg->hw_protocol = entry->skb->protocol; |
|
if (entry->info->indev) |
strcpy(pmsg->indev_name, entry->info->indev->name); |
else |
pmsg->indev_name[0] = '\0'; |
|
if (entry->info->outdev) |
strcpy(pmsg->outdev_name, entry->info->outdev->name); |
else |
pmsg->outdev_name[0] = '\0'; |
|
if (entry->info->indev && entry->skb->dev) { |
pmsg->hw_type = entry->skb->dev->type; |
if (entry->skb->dev->hard_header_parse) |
pmsg->hw_addrlen = |
entry->skb->dev->hard_header_parse(entry->skb, |
pmsg->hw_addr); |
} |
|
if (data_len) |
memcpy(pmsg->payload, entry->skb->data, data_len); |
|
nlh->nlmsg_len = skb->tail - old_tail; |
return skb; |
|
nlmsg_failure: |
if (skb) |
kfree_skb(skb); |
*errp = -EINVAL; |
printk(KERN_ERR "ip6_queue: error creating packet message\n"); |
return NULL; |
} |
|
static int |
ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data) |
{ |
int status = -EINVAL; |
struct sk_buff *nskb; |
struct ipq_queue_entry *entry; |
|
if (copy_mode == IPQ_COPY_NONE) |
return -EAGAIN; |
|
entry = kmalloc(sizeof(*entry), GFP_ATOMIC); |
if (entry == NULL) { |
printk(KERN_ERR "ip6_queue: OOM in ipq_enqueue_packet()\n"); |
return -ENOMEM; |
} |
|
entry->info = info; |
entry->skb = skb; |
|
if (entry->info->hook == NF_IP_LOCAL_OUT) { |
struct ipv6hdr *iph = skb->nh.ipv6h; |
|
entry->rt_info.daddr = iph->daddr; |
entry->rt_info.saddr = iph->saddr; |
} |
|
nskb = ipq_build_packet_message(entry, &status); |
if (nskb == NULL) |
goto err_out_free; |
|
write_lock_bh(&queue_lock); |
|
if (!peer_pid) |
goto err_out_free_nskb; |
|
/* netlink_unicast will either free the nskb or attach it to a socket */ |
status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); |
if (status < 0) |
goto err_out_unlock; |
|
status = __ipq_enqueue_entry(entry); |
if (status < 0) |
goto err_out_unlock; |
|
write_unlock_bh(&queue_lock); |
return status; |
|
err_out_free_nskb: |
kfree_skb(nskb); |
|
err_out_unlock: |
write_unlock_bh(&queue_lock); |
|
err_out_free: |
kfree(entry); |
return status; |
} |
|
static int |
ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) |
{ |
int diff; |
struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; |
|
if (v->data_len < sizeof(*user_iph)) |
return 0; |
diff = v->data_len - e->skb->len; |
if (diff < 0) |
skb_trim(e->skb, v->data_len); |
else if (diff > 0) { |
if (v->data_len > 0xFFFF) |
return -EINVAL; |
if (diff > skb_tailroom(e->skb)) { |
struct sk_buff *newskb; |
|
newskb = skb_copy_expand(e->skb, |
skb_headroom(e->skb), |
diff, |
GFP_ATOMIC); |
if (newskb == NULL) { |
printk(KERN_WARNING "ip6_queue: OOM " |
"in mangle, dropping packet\n"); |
return -ENOMEM; |
} |
if (e->skb->sk) |
skb_set_owner_w(newskb, e->skb->sk); |
kfree_skb(e->skb); |
e->skb = newskb; |
} |
skb_put(e->skb, diff); |
} |
memcpy(e->skb->data, v->payload, v->data_len); |
e->skb->nfcache |= NFC_ALTERED; |
|
/* |
* Extra routing may needed on local out, as the QUEUE target never |
* returns control to the table. |
* Not a nice way to cmp, but works |
*/ |
if (e->info->hook == NF_IP_LOCAL_OUT) { |
struct ipv6hdr *iph = e->skb->nh.ipv6h; |
if (ipv6_addr_cmp(&iph->daddr, &e->rt_info.daddr) || |
ipv6_addr_cmp(&iph->saddr, &e->rt_info.saddr)) |
return ip6_route_me_harder(e->skb); |
} |
return 0; |
} |
|
static inline int |
id_cmp(struct ipq_queue_entry *e, unsigned long id) |
{ |
return (id == (unsigned long )e); |
} |
|
static int |
ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) |
{ |
struct ipq_queue_entry *entry; |
|
if (vmsg->value > NF_MAX_VERDICT) |
return -EINVAL; |
|
entry = ipq_find_dequeue_entry(id_cmp, vmsg->id); |
if (entry == NULL) |
return -ENOENT; |
else { |
int verdict = vmsg->value; |
|
if (vmsg->data_len && vmsg->data_len == len) |
if (ipq_mangle_ipv6(vmsg, entry) < 0) |
verdict = NF_DROP; |
|
ipq_issue_verdict(entry, verdict); |
return 0; |
} |
} |
|
static int |
ipq_set_mode(unsigned char mode, unsigned int range) |
{ |
int status; |
|
write_lock_bh(&queue_lock); |
status = __ipq_set_mode(mode, range); |
write_unlock_bh(&queue_lock); |
return status; |
} |
|
static int |
ipq_receive_peer(struct ipq_peer_msg *pmsg, |
unsigned char type, unsigned int len) |
{ |
int status = 0; |
|
if (len < sizeof(*pmsg)) |
return -EINVAL; |
|
switch (type) { |
case IPQM_MODE: |
status = ipq_set_mode(pmsg->msg.mode.value, |
pmsg->msg.mode.range); |
break; |
|
case IPQM_VERDICT: |
if (pmsg->msg.verdict.value > NF_MAX_VERDICT) |
status = -EINVAL; |
else |
status = ipq_set_verdict(&pmsg->msg.verdict, |
len - sizeof(*pmsg)); |
break; |
default: |
status = -EINVAL; |
} |
return status; |
} |
|
static int |
dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex) |
{ |
if (entry->info->indev) |
if (entry->info->indev->ifindex == ifindex) |
return 1; |
|
if (entry->info->outdev) |
if (entry->info->outdev->ifindex == ifindex) |
return 1; |
|
return 0; |
} |
|
static void |
ipq_dev_drop(int ifindex) |
{ |
struct ipq_queue_entry *entry; |
|
while ((entry = ipq_find_dequeue_entry(dev_cmp, ifindex)) != NULL) |
ipq_issue_verdict(entry, NF_DROP); |
} |
|
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) |
|
static inline void |
ipq_rcv_skb(struct sk_buff *skb) |
{ |
int status, type, pid, flags, nlmsglen, skblen; |
struct nlmsghdr *nlh; |
|
skblen = skb->len; |
if (skblen < sizeof(*nlh)) |
return; |
|
nlh = (struct nlmsghdr *)skb->data; |
nlmsglen = nlh->nlmsg_len; |
if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) |
return; |
|
pid = nlh->nlmsg_pid; |
flags = nlh->nlmsg_flags; |
|
if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) |
RCV_SKB_FAIL(-EINVAL); |
|
if (flags & MSG_TRUNC) |
RCV_SKB_FAIL(-ECOMM); |
|
type = nlh->nlmsg_type; |
if (type < NLMSG_NOOP || type >= IPQM_MAX) |
RCV_SKB_FAIL(-EINVAL); |
|
if (type <= IPQM_BASE) |
return; |
|
if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) |
RCV_SKB_FAIL(-EPERM); |
|
write_lock_bh(&queue_lock); |
|
if (peer_pid) { |
if (peer_pid != pid) { |
write_unlock_bh(&queue_lock); |
RCV_SKB_FAIL(-EBUSY); |
} |
} |
else |
peer_pid = pid; |
|
write_unlock_bh(&queue_lock); |
|
status = ipq_receive_peer(NLMSG_DATA(nlh), type, |
skblen - NLMSG_LENGTH(0)); |
if (status < 0) |
RCV_SKB_FAIL(status); |
|
if (flags & NLM_F_ACK) |
netlink_ack(skb, nlh, 0); |
return; |
} |
|
static void |
ipq_rcv_sk(struct sock *sk, int len) |
{ |
do { |
struct sk_buff *skb; |
|
if (down_trylock(&ipqnl_sem)) |
return; |
|
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { |
ipq_rcv_skb(skb); |
kfree_skb(skb); |
} |
|
up(&ipqnl_sem); |
|
} while (ipqnl && ipqnl->receive_queue.qlen); |
} |
|
static int |
ipq_rcv_dev_event(struct notifier_block *this, |
unsigned long event, void *ptr) |
{ |
struct net_device *dev = ptr; |
|
/* Drop any packets associated with the downed device */ |
if (event == NETDEV_DOWN) |
ipq_dev_drop(dev->ifindex); |
return NOTIFY_DONE; |
} |
|
static struct notifier_block ipq_dev_notifier = { |
ipq_rcv_dev_event, |
NULL, |
0 |
}; |
|
static int |
ipq_rcv_nl_event(struct notifier_block *this, |
unsigned long event, void *ptr) |
{ |
struct netlink_notify *n = ptr; |
|
if (event == NETLINK_URELEASE && |
n->protocol == NETLINK_IP6_FW && n->pid) { |
write_lock_bh(&queue_lock); |
if (n->pid == peer_pid) |
__ipq_reset(); |
write_unlock_bh(&queue_lock); |
} |
return NOTIFY_DONE; |
} |
|
static struct notifier_block ipq_nl_notifier = { |
ipq_rcv_nl_event, |
NULL, |
0 |
}; |
|
static struct ctl_table_header *ipq_sysctl_header; |
|
static ctl_table ipq_table[] = { |
{ NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &queue_maxlen, |
sizeof(queue_maxlen), 0644, NULL, proc_dointvec }, |
{ 0 } |
}; |
|
static ctl_table ipq_dir_table[] = { |
{NET_IPV6, "ipv6", NULL, 0, 0555, ipq_table, 0, 0, 0, 0, 0}, |
{ 0 } |
}; |
|
static ctl_table ipq_root_table[] = { |
{CTL_NET, "net", NULL, 0, 0555, ipq_dir_table, 0, 0, 0, 0, 0}, |
{ 0 } |
}; |
|
static int |
ipq_get_info(char *buffer, char **start, off_t offset, int length) |
{ |
int len; |
|
read_lock_bh(&queue_lock); |
|
len = sprintf(buffer, |
"Peer PID : %d\n" |
"Copy mode : %hu\n" |
"Copy range : %u\n" |
"Queue length : %u\n" |
"Queue max. length : %u\n", |
peer_pid, |
copy_mode, |
copy_range, |
queue_total, |
queue_maxlen); |
|
read_unlock_bh(&queue_lock); |
|
*start = buffer + offset; |
len -= offset; |
if (len > length) |
len = length; |
else if (len < 0) |
len = 0; |
return len; |
} |
|
static int |
init_or_cleanup(int init) |
{ |
int status = -ENOMEM; |
struct proc_dir_entry *proc; |
|
if (!init) |
goto cleanup; |
|
netlink_register_notifier(&ipq_nl_notifier); |
ipqnl = netlink_kernel_create(NETLINK_IP6_FW, ipq_rcv_sk); |
if (ipqnl == NULL) { |
printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); |
goto cleanup_netlink_notifier; |
} |
|
proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info); |
if (proc) |
proc->owner = THIS_MODULE; |
else { |
printk(KERN_ERR "ip6_queue: failed to create proc entry\n"); |
goto cleanup_ipqnl; |
} |
|
register_netdevice_notifier(&ipq_dev_notifier); |
ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0); |
|
status = nf_register_queue_handler(PF_INET6, ipq_enqueue_packet, NULL); |
if (status < 0) { |
printk(KERN_ERR "ip6_queue: failed to register queue handler\n"); |
goto cleanup_sysctl; |
} |
return status; |
|
cleanup: |
nf_unregister_queue_handler(PF_INET6); |
br_write_lock_bh(BR_NETPROTO_LOCK); |
br_write_unlock_bh(BR_NETPROTO_LOCK); |
ipq_flush(NF_DROP); |
|
cleanup_sysctl: |
unregister_sysctl_table(ipq_sysctl_header); |
unregister_netdevice_notifier(&ipq_dev_notifier); |
proc_net_remove(IPQ_PROC_FS_NAME); |
|
cleanup_ipqnl: |
sock_release(ipqnl->socket); |
down(&ipqnl_sem); |
up(&ipqnl_sem); |
|
cleanup_netlink_notifier: |
netlink_unregister_notifier(&ipq_nl_notifier); |
return status; |
} |
|
static int __init init(void) |
{ |
|
return init_or_cleanup(1); |
} |
|
static void __exit fini(void) |
{ |
init_or_cleanup(0); |
} |
|
MODULE_DESCRIPTION("IPv6 packet queue handler"); |
MODULE_LICENSE("GPL"); |
|
module_init(init); |
module_exit(fini); |
/ip6t_mac.c
0,0 → 1,66
/* Kernel module to match MAC address parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/if_ether.h> |
|
#include <linux/netfilter_ipv6/ip6t_mac.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_mac_info *info = matchinfo; |
|
/* Is mac pointer valid? */ |
return (skb->mac.raw >= skb->head |
&& (skb->mac.raw + ETH_HLEN) <= skb->data |
/* If so, compare... */ |
&& ((memcmp(skb->mac.ethernet->h_source, info->srcaddr, ETH_ALEN) |
== 0) ^ info->invert)); |
} |
|
static int |
ip6t_mac_checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (hook_mask |
& ~((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_IN) |
| (1 << NF_IP6_FORWARD))) { |
printk("ip6t_mac: only valid for PRE_ROUTING, LOCAL_IN or" |
" FORWARD\n"); |
return 0; |
} |
|
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_mac_info))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match mac_match |
= { { NULL, NULL }, "mac", &match, &ip6t_mac_checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&mac_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&mac_match); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("MAC address matching module for IPv6"); |
/ip6t_hbh.c
0,0 → 1,276
/* Kernel module to match Hop-by-Hop and Destination parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <asm/byteorder.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_opts.h> |
|
#define LOW(n) (n & 0x00FF) |
|
#define HOPBYHOP 1 |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
#if HOPBYHOP |
MODULE_DESCRIPTION("IPv6 HbH match"); |
#else |
MODULE_DESCRIPTION("IPv6 DST match"); |
#endif |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
/* |
* (Type & 0xC0) >> 6 |
* 0 -> ignorable |
* 1 -> must drop the packet |
* 2 -> send ICMP PARM PROB regardless and drop packet |
* 3 -> Send ICMP if not a multicast address and drop packet |
* (Type & 0x20) >> 5 |
* 0 -> invariant |
* 1 -> can change the routing |
* (Type & 0x1F) Type |
* 0 -> PAD0 (only 1 byte!) |
* 1 -> PAD1 LENGTH info (total length = length + 2) |
* C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k ) |
* 5 -> RTALERT 2 x x |
*/ |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct ipv6_opt_hdr *optsh = NULL; |
const struct ip6t_opts *optinfo = matchinfo; |
unsigned int temp; |
unsigned int len; |
u8 nexthdr; |
unsigned int ptr; |
unsigned int hdrlen = 0; |
unsigned int ret = 0; |
u_int16_t *optdesc = NULL; |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
|
DEBUGP("ipv6_opts header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
break; |
} |
|
hdr=(void *)(skb->data)+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* OPTS -> evaluate */ |
#if HOPBYHOP |
if (nexthdr == NEXTHDR_HOP) { |
temp |= MASK_HOPOPTS; |
#else |
if (nexthdr == NEXTHDR_DEST) { |
temp |= MASK_DSTOPTS; |
#endif |
break; |
} |
|
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_opts: new pointer is too large! \n"); |
break; |
} |
} |
|
/* OPTIONS header not found */ |
#if HOPBYHOP |
if ( temp != MASK_HOPOPTS ) return 0; |
#else |
if ( temp != MASK_DSTOPTS ) return 0; |
#endif |
|
if (len < (int)sizeof(struct ipv6_opt_hdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
if (len < hdrlen){ |
/* Packet smaller than it's length field */ |
return 0; |
} |
|
optsh=(void *)(skb->data)+ptr; |
|
DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen); |
|
DEBUGP("len %02X %04X %02X ", |
optinfo->hdrlen, hdrlen, |
(!(optinfo->flags & IP6T_OPTS_LEN) || |
((optinfo->hdrlen == hdrlen) ^ |
!!(optinfo->invflags & IP6T_OPTS_INV_LEN)))); |
|
ret = (optsh != NULL) |
&& |
(!(optinfo->flags & IP6T_OPTS_LEN) || |
((optinfo->hdrlen == hdrlen) ^ |
!!(optinfo->invflags & IP6T_OPTS_INV_LEN))); |
|
temp = len = 0; |
ptr += 2; |
hdrlen -= 2; |
if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){ |
return ret; |
} else if (optinfo->flags & IP6T_OPTS_NSTRICT) { |
DEBUGP("Not strict - not implemented"); |
} else { |
DEBUGP("Strict "); |
DEBUGP("#%d ",optinfo->optsnr); |
for(temp=0; temp<optinfo->optsnr; temp++){ |
optdesc = (void *)(skb->data)+ptr; |
/* Type check */ |
if ( (unsigned char)*optdesc != |
(optinfo->opts[temp] & 0xFF00)>>8 ){ |
DEBUGP("Tbad %02X %02X\n", |
(unsigned char)*optdesc, |
(optinfo->opts[temp] & |
0xFF00)>>8); |
return 0; |
} else { |
DEBUGP("Tok "); |
} |
/* Length check */ |
if (((optinfo->opts[temp] & 0x00FF) != 0xFF) && |
(unsigned char)*optdesc != 0){ |
if ( ntohs((u16)*optdesc) != |
optinfo->opts[temp] ){ |
DEBUGP("Lbad %02X %04X %04X\n", |
(unsigned char)*optdesc, |
ntohs((u16)*optdesc), |
optinfo->opts[temp]); |
return 0; |
} else { |
DEBUGP("Lok "); |
} |
} |
/* Step to the next */ |
if ((unsigned char)*optdesc == 0){ |
DEBUGP("PAD0 \n"); |
ptr++; |
hdrlen--; |
} else { |
ptr += LOW(ntohs(*optdesc)); |
hdrlen -= LOW(ntohs(*optdesc)); |
DEBUGP("len%04X \n", |
LOW(ntohs(*optdesc))); |
} |
if (ptr > skb->len || ( !hdrlen && |
(temp != optinfo->optsnr - 1))) { |
DEBUGP("new pointer is too large! \n"); |
break; |
} |
} |
if (temp == optinfo->optsnr) |
return ret; |
else return 0; |
} |
|
return 0; |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_opts *optsinfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) { |
DEBUGP("ip6t_opts: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts))); |
return 0; |
} |
if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) { |
DEBUGP("ip6t_opts: unknown flags %X\n", |
optsinfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match opts_match |
#if HOPBYHOP |
= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE }; |
#else |
= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE }; |
#endif |
|
static int __init init(void) |
{ |
return ip6t_register_match(&opts_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&opts_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/ip6t_hl.c
0,0 → 1,74
/* |
* Hop Limit matching module |
* Maciej Soltysiak <solt@dns.toxicfilms.tv> |
* Based on HW's ttl module |
* |
* This software is distributed under the terms GNU GPL |
*/ |
|
#include <linux/module.h> |
#include <linux/skbuff.h> |
|
#include <linux/netfilter_ipv6/ip6t_hl.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>"); |
MODULE_DESCRIPTION("IP tables Hop Limit matching module"); |
MODULE_LICENSE("GPL"); |
|
static int match(const struct sk_buff *skb, const struct net_device *in, |
const struct net_device *out, const void *matchinfo, |
int offset, const void *hdr, u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_hl_info *info = matchinfo; |
const struct ipv6hdr *ip6h = skb->nh.ipv6h; |
|
switch (info->mode) { |
case IP6T_HL_EQ: |
return (ip6h->hop_limit == info->hop_limit); |
break; |
case IP6T_HL_NE: |
return (!(ip6h->hop_limit == info->hop_limit)); |
break; |
case IP6T_HL_LT: |
return (ip6h->hop_limit < info->hop_limit); |
break; |
case IP6T_HL_GT: |
return (ip6h->hop_limit > info->hop_limit); |
break; |
default: |
printk(KERN_WARNING "ip6t_hl: unknown mode %d\n", |
info->mode); |
return 0; |
} |
|
return 0; |
} |
|
static int checkentry(const char *tablename, const struct ip6t_ip6 *ip, |
void *matchinfo, unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_hl_info))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match hl_match = { { NULL, NULL }, "hl", &match, |
&checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&hl_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&hl_match); |
|
} |
|
module_init(init); |
module_exit(fini); |
/ip6table_filter.c
0,0 → 1,184
/* |
* This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x. |
* |
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling |
*/ |
#include <linux/module.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
#define FILTER_VALID_HOOKS ((1 << NF_IP6_LOCAL_IN) | (1 << NF_IP6_FORWARD) | (1 << NF_IP6_LOCAL_OUT)) |
|
/* Standard entry. */ |
struct ip6t_standard |
{ |
struct ip6t_entry entry; |
struct ip6t_standard_target target; |
}; |
|
struct ip6t_error_target |
{ |
struct ip6t_entry_target target; |
char errorname[IP6T_FUNCTION_MAXNAMELEN]; |
}; |
|
struct ip6t_error |
{ |
struct ip6t_entry entry; |
struct ip6t_error_target target; |
}; |
|
static struct |
{ |
struct ip6t_replace repl; |
struct ip6t_standard entries[3]; |
struct ip6t_error term; |
} initial_table __initdata |
= { { "filter", FILTER_VALID_HOOKS, 4, |
sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), |
{ [NF_IP6_LOCAL_IN] 0, |
[NF_IP6_FORWARD] sizeof(struct ip6t_standard), |
[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 2 }, |
{ [NF_IP6_LOCAL_IN] 0, |
[NF_IP6_FORWARD] sizeof(struct ip6t_standard), |
[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 2 }, |
0, NULL, { } }, |
{ |
/* LOCAL_IN */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* FORWARD */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* LOCAL_OUT */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } } |
}, |
/* ERROR */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_error), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, |
{ } }, |
"ERROR" |
} |
} |
}; |
|
static struct ip6t_table packet_filter |
= { { NULL, NULL }, "filter", &initial_table.repl, |
FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; |
|
/* The work comes in here from netfilter.c. */ |
static unsigned int |
ip6t_hook(unsigned int hook, |
struct sk_buff **pskb, |
const struct net_device *in, |
const struct net_device *out, |
int (*okfn)(struct sk_buff *)) |
{ |
return ip6t_do_table(pskb, hook, in, out, &packet_filter, NULL); |
} |
|
static unsigned int |
ip6t_local_out_hook(unsigned int hook, |
struct sk_buff **pskb, |
const struct net_device *in, |
const struct net_device *out, |
int (*okfn)(struct sk_buff *)) |
{ |
#if 0 |
/* root is playing with raw sockets. */ |
if ((*pskb)->len < sizeof(struct iphdr) |
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { |
if (net_ratelimit()) |
printk("ip6t_hook: happy cracking.\n"); |
return NF_ACCEPT; |
} |
#endif |
|
return ip6t_do_table(pskb, hook, in, out, &packet_filter, NULL); |
} |
|
static struct nf_hook_ops ip6t_ops[] |
= { { { NULL, NULL }, ip6t_hook, PF_INET6, NF_IP6_LOCAL_IN, NF_IP6_PRI_FILTER }, |
{ { NULL, NULL }, ip6t_hook, PF_INET6, NF_IP6_FORWARD, NF_IP6_PRI_FILTER }, |
{ { NULL, NULL }, ip6t_local_out_hook, PF_INET6, NF_IP6_LOCAL_OUT, |
NF_IP6_PRI_FILTER } |
}; |
|
/* Default to forward because I got too much mail already. */ |
static int forward = NF_ACCEPT; |
MODULE_PARM(forward, "i"); |
|
static int __init init(void) |
{ |
int ret; |
|
if (forward < 0 || forward > NF_MAX_VERDICT) { |
printk("iptables forward must be 0 or 1\n"); |
return -EINVAL; |
} |
|
/* Entry 1 is the FORWARD hook */ |
initial_table.entries[1].target.verdict = -forward - 1; |
|
/* Register table */ |
ret = ip6t_register_table(&packet_filter); |
if (ret < 0) |
return ret; |
|
/* Register hooks */ |
ret = nf_register_hook(&ip6t_ops[0]); |
if (ret < 0) |
goto cleanup_table; |
|
ret = nf_register_hook(&ip6t_ops[1]); |
if (ret < 0) |
goto cleanup_hook0; |
|
ret = nf_register_hook(&ip6t_ops[2]); |
if (ret < 0) |
goto cleanup_hook1; |
|
return ret; |
|
cleanup_hook1: |
nf_unregister_hook(&ip6t_ops[1]); |
cleanup_hook0: |
nf_unregister_hook(&ip6t_ops[0]); |
cleanup_table: |
ip6t_unregister_table(&packet_filter); |
|
return ret; |
} |
|
static void __exit fini(void) |
{ |
unsigned int i; |
|
for (i = 0; i < sizeof(ip6t_ops)/sizeof(struct nf_hook_ops); i++) |
nf_unregister_hook(&ip6t_ops[i]); |
|
ip6t_unregister_table(&packet_filter); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
/ip6t_limit.c
0,0 → 1,136
/* Kernel module to control the rate |
* |
|
|
* |
* 2 September 1999: Changed from the target RATE to the match |
* `limit', removed logging. Did I mention that |
* Alexey is a fucking genius? |
* Rusty Russell (rusty@rustcorp.com.au). */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/spinlock.h> |
#include <linux/interrupt.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_limit.h> |
|
/* The algorithm used is the Simple Token Bucket Filter (TBF) |
* see net/sched/sch_tbf.c in the linux source tree |
*/ |
|
static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED; |
|
/* Rusty: This is my (non-mathematically-inclined) understanding of |
this algorithm. The `average rate' in jiffies becomes your initial |
amount of credit `credit' and the most credit you can ever have |
`credit_cap'. The `peak rate' becomes the cost of passing the |
test, `cost'. |
|
`prev' tracks the last packet hit: you gain one credit per jiffy. |
If you get credit balance more than this, the extra credit is |
discarded. Every time the match passes, you lose `cost' credits; |
if you don't have that many, the test fails. |
|
See Alexey's formal explanation in net/sched/sch_tbf.c. |
|
To avoid underflow, we multiply by 128 (ie. you get 128 credits per |
jiffy). Hence a cost of 2^32-1, means one pass per 32768 seconds |
at 1024HZ (or one every 9 hours). A cost of 1 means 12800 passes |
per second at 100HZ. */ |
|
#define CREDITS_PER_JIFFY 128 |
|
static int |
ip6t_limit_match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct ip6t_rateinfo *r = ((struct ip6t_rateinfo *)matchinfo)->master; |
unsigned long now = jiffies; |
|
spin_lock_bh(&limit_lock); |
r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY; |
if (r->credit > r->credit_cap) |
r->credit = r->credit_cap; |
|
if (r->credit >= r->cost) { |
/* We're not limited. */ |
r->credit -= r->cost; |
spin_unlock_bh(&limit_lock); |
return 1; |
} |
|
spin_unlock_bh(&limit_lock); |
return 0; |
} |
|
/* Precision saver. */ |
static u_int32_t |
user2credits(u_int32_t user) |
{ |
/* If multiplying would overflow... */ |
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) |
/* Divide first. */ |
return (user / IP6T_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; |
|
return (user * HZ * CREDITS_PER_JIFFY) / IP6T_LIMIT_SCALE; |
} |
|
static int |
ip6t_limit_checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
struct ip6t_rateinfo *r = matchinfo; |
|
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_rateinfo))) |
return 0; |
|
/* Check for overflow. */ |
if (r->burst == 0 |
|| user2credits(r->avg * r->burst) < user2credits(r->avg)) { |
printk("Call rusty: overflow in ip6t_limit: %u/%u\n", |
r->avg, r->burst); |
return 0; |
} |
|
/* User avg in seconds * IP6T_LIMIT_SCALE: convert to jiffies * |
128. */ |
r->prev = jiffies; |
r->credit = user2credits(r->avg * r->burst); /* Credits full. */ |
r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ |
r->cost = user2credits(r->avg); |
|
/* For SMP, we only want to use one set of counters. */ |
r->master = r; |
|
return 1; |
} |
|
static struct ip6t_match ip6t_limit_reg |
= { { NULL, NULL }, "limit", ip6t_limit_match, ip6t_limit_checkentry, NULL, |
THIS_MODULE }; |
|
static int __init init(void) |
{ |
if (ip6t_register_match(&ip6t_limit_reg)) |
return -EINVAL; |
return 0; |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&ip6t_limit_reg); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
/ip6t_frag.c
0,0 → 1,239
/* Kernel module to match FRAG parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <asm/byteorder.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_frag.h> |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("IPv6 FRAG match"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
#if 0 |
#if BYTE_ORDER == BIG_ENDIAN |
#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ |
#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ |
#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ |
#else /* BYTE_ORDER == LITTLE_ENDIAN */ |
#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ |
#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ |
#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ |
#endif |
#endif |
|
#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ |
#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ |
#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ |
|
struct fraghdr { |
__u8 nexthdr; |
__u8 hdrlen; |
__u16 info; |
__u32 id; |
}; |
|
/* Returns 1 if the id is matched by the range, 0 otherwise */ |
static inline int |
id_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert) |
{ |
int r=0; |
DEBUGP("frag id_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ', |
min,id,max); |
r=(id >= min && id <= max) ^ invert; |
DEBUGP(" result %s\n",r? "PASS" : "FAILED"); |
return r; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct fraghdr *frag = NULL; |
const struct ip6t_frag *fraginfo = matchinfo; |
unsigned int temp; |
int len; |
u8 nexthdr; |
unsigned int ptr; |
unsigned int hdrlen = 0; |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
|
DEBUGP("ipv6_frag header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
break; |
} |
|
hdr=(struct ipv6_opt_hdr *)skb->data+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* FRAG -> evaluate */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
temp |= MASK_FRAGMENT; |
break; |
} |
|
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_frag: new pointer too large! \n"); |
break; |
} |
} |
|
/* FRAG header not found */ |
if ( temp != MASK_FRAGMENT ) return 0; |
|
if (len < (int)sizeof(struct fraghdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
frag = (struct fraghdr *) (skb->data + ptr); |
|
DEBUGP("IPv6 FRAG LEN %u %u ", hdrlen, frag->hdrlen); |
DEBUGP("INFO %04X ", frag->info); |
DEBUGP("OFFSET %04X ", frag->info & IP6F_OFF_MASK); |
DEBUGP("RES %04X ", frag->info & IP6F_RESERVED_MASK); |
DEBUGP("MF %04X ", frag->info & IP6F_MORE_FRAG); |
DEBUGP("ID %u %08X\n", ntohl(frag->id), ntohl(frag->id)); |
|
DEBUGP("IPv6 FRAG id %02X ", |
(id_match(fraginfo->ids[0], fraginfo->ids[1], |
ntohl(frag->id), |
!!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))); |
DEBUGP("len %02X %04X %02X ", |
fraginfo->hdrlen, hdrlen, |
(!fraginfo->hdrlen || |
(fraginfo->hdrlen == hdrlen) ^ |
!!(fraginfo->invflags & IP6T_FRAG_INV_LEN))); |
DEBUGP("res %02X %02X %02X ", |
(fraginfo->flags & IP6T_FRAG_RES), frag->info & IP6F_RESERVED_MASK, |
!((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK))); |
DEBUGP("first %02X %02X %02X ", |
(fraginfo->flags & IP6T_FRAG_FST), frag->info & IP6F_OFF_MASK, |
!((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK))); |
DEBUGP("mf %02X %02X %02X ", |
(fraginfo->flags & IP6T_FRAG_MF), frag->info & IP6F_MORE_FRAG, |
!((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG)))); |
DEBUGP("last %02X %02X %02X\n", |
(fraginfo->flags & IP6T_FRAG_NMF), frag->info & IP6F_MORE_FRAG, |
!((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG))); |
|
return (frag != NULL) |
&& |
(id_match(fraginfo->ids[0], fraginfo->ids[1], |
ntohl(frag->id), |
!!(fraginfo->invflags & IP6T_FRAG_INV_IDS))) |
&& |
(!fraginfo->hdrlen || |
(fraginfo->hdrlen == hdrlen) ^ |
!!(fraginfo->invflags & IP6T_FRAG_INV_LEN)) |
&& |
!((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK)) |
&& |
!((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK)) |
&& |
!((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG))) |
&& |
!((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_frag *fraginfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_frag))) { |
DEBUGP("ip6t_frag: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_frag))); |
return 0; |
} |
if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) { |
DEBUGP("ip6t_frag: unknown flags %X\n", |
fraginfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match frag_match |
= { { NULL, NULL }, "frag", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&frag_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&frag_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/ip6t_LOG.c
0,0 → 1,382
/* |
* This is a module which is used for logging packets. |
*/ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ip.h> |
#include <linux/spinlock.h> |
#include <linux/icmpv6.h> |
#include <net/udp.h> |
#include <net/tcp.h> |
#include <net/ipv6.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); |
MODULE_DESCRIPTION("IP6 tables LOG target module"); |
MODULE_LICENSE("GPL"); |
|
struct in_device; |
#include <net/route.h> |
#include <linux/netfilter_ipv6/ip6t_LOG.h> |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
#define NIP6(addr) \ |
ntohs((addr).s6_addr16[0]), \ |
ntohs((addr).s6_addr16[1]), \ |
ntohs((addr).s6_addr16[2]), \ |
ntohs((addr).s6_addr16[3]), \ |
ntohs((addr).s6_addr16[4]), \ |
ntohs((addr).s6_addr16[5]), \ |
ntohs((addr).s6_addr16[6]), \ |
ntohs((addr).s6_addr16[7]) |
|
struct esphdr { |
__u32 spi; |
}; /* FIXME evil kludge */ |
|
/* Use lock to serialize, so printks don't overlap */ |
static spinlock_t log_lock = SPIN_LOCK_UNLOCKED; |
|
/* takes in current header and pointer to the header */ |
/* if another header exists, sets hdrptr to the next header |
and returns the new header value, else returns 0 */ |
static u_int8_t ip6_nexthdr(u_int8_t currenthdr, u_int8_t **hdrptr) |
{ |
u_int8_t hdrlen, nexthdr = 0; |
|
switch(currenthdr){ |
case IPPROTO_AH: |
/* whoever decided to do the length of AUTH for ipv6 |
in 32bit units unlike other headers should be beaten... |
repeatedly...with a large stick...no, an even LARGER |
stick...no, you're still not thinking big enough */ |
nexthdr = **hdrptr; |
hdrlen = *hdrptr[1] * 4 + 8; |
*hdrptr = *hdrptr + hdrlen; |
break; |
/*stupid rfc2402 */ |
case IPPROTO_DSTOPTS: |
case IPPROTO_ROUTING: |
case IPPROTO_HOPOPTS: |
nexthdr = **hdrptr; |
hdrlen = *hdrptr[1] * 8 + 8; |
*hdrptr = *hdrptr + hdrlen; |
break; |
case IPPROTO_FRAGMENT: |
nexthdr = **hdrptr; |
*hdrptr = *hdrptr + 8; |
break; |
} |
return nexthdr; |
|
} |
|
/* One level of recursion won't kill us */ |
static void dump_packet(const struct ip6t_log_info *info, |
struct ipv6hdr *ipv6h, int recurse) |
{ |
u_int8_t currenthdr = ipv6h->nexthdr; |
u_int8_t *hdrptr; |
int fragment; |
|
/* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000" */ |
printk("SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->saddr)); |
printk("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); |
|
/* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ |
printk("LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", |
ntohs(ipv6h->payload_len) + sizeof(struct ipv6hdr), |
(ntohl(*(u_int32_t *)ipv6h) & 0x0ff00000) >> 20, |
ipv6h->hop_limit, |
(ntohl(*(u_int32_t *)ipv6h) & 0x000fffff)); |
|
fragment = 0; |
hdrptr = (u_int8_t *)(ipv6h + 1); |
while (currenthdr) { |
if ((currenthdr == IPPROTO_TCP) || |
(currenthdr == IPPROTO_UDP) || |
(currenthdr == IPPROTO_ICMPV6)) |
break; |
/* Max length: 48 "OPT (...) " */ |
printk("OPT ( "); |
switch (currenthdr) { |
case IPPROTO_FRAGMENT: { |
struct frag_hdr *fhdr = (struct frag_hdr *)hdrptr; |
|
/* Max length: 11 "FRAG:65535 " */ |
printk("FRAG:%u ", ntohs(fhdr->frag_off) & 0xFFF8); |
|
/* Max length: 11 "INCOMPLETE " */ |
if (fhdr->frag_off & htons(0x0001)) |
printk("INCOMPLETE "); |
|
printk("ID:%08x ", fhdr->identification); |
|
if (ntohs(fhdr->frag_off) & 0xFFF8) |
fragment = 1; |
|
break; |
} |
case IPPROTO_DSTOPTS: |
case IPPROTO_ROUTING: |
case IPPROTO_HOPOPTS: |
break; |
/* Max Length */ |
case IPPROTO_AH: |
case IPPROTO_ESP: |
if (info->logflags & IP6T_LOG_IPOPT) { |
struct esphdr *esph = (struct esphdr *)hdrptr; |
int esp = (currenthdr == IPPROTO_ESP); |
|
/* Max length: 4 "ESP " */ |
printk("%s ",esp ? "ESP" : "AH"); |
|
/* Length: 15 "SPI=0xF1234567 " */ |
printk("SPI=0x%x ", ntohl(esph->spi) ); |
break; |
} |
default: |
break; |
} |
printk(") "); |
currenthdr = ip6_nexthdr(currenthdr, &hdrptr); |
} |
|
switch (currenthdr) { |
case IPPROTO_TCP: { |
struct tcphdr *tcph = (struct tcphdr *)hdrptr; |
|
/* Max length: 10 "PROTO=TCP " */ |
printk("PROTO=TCP "); |
|
if (fragment) |
break; |
|
/* Max length: 20 "SPT=65535 DPT=65535 " */ |
printk("SPT=%u DPT=%u ", |
ntohs(tcph->source), ntohs(tcph->dest)); |
/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ |
if (info->logflags & IP6T_LOG_TCPSEQ) |
printk("SEQ=%u ACK=%u ", |
ntohl(tcph->seq), ntohl(tcph->ack_seq)); |
/* Max length: 13 "WINDOW=65535 " */ |
printk("WINDOW=%u ", ntohs(tcph->window)); |
/* Max length: 9 "RES=0x3F " */ |
printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22)); |
/* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ |
if (tcph->cwr) |
printk("CWR "); |
if (tcph->ece) |
printk("ECE "); |
if (tcph->urg) |
printk("URG "); |
if (tcph->ack) |
printk("ACK "); |
if (tcph->psh) |
printk("PSH "); |
if (tcph->rst) |
printk("RST "); |
if (tcph->syn) |
printk("SYN "); |
if (tcph->fin) |
printk("FIN "); |
/* Max length: 11 "URGP=65535 " */ |
printk("URGP=%u ", ntohs(tcph->urg_ptr)); |
|
if ((info->logflags & IP6T_LOG_TCPOPT) |
&& tcph->doff * 4 != sizeof(struct tcphdr)) { |
unsigned int i; |
|
/* Max length: 127 "OPT (" 15*4*2chars ") " */ |
printk("OPT ("); |
for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++) |
printk("%02X", ((u_int8_t *)tcph)[i]); |
printk(") "); |
} |
break; |
} |
case IPPROTO_UDP: { |
struct udphdr *udph = (struct udphdr *)hdrptr; |
|
/* Max length: 10 "PROTO=UDP " */ |
printk("PROTO=UDP "); |
|
if (fragment) |
break; |
|
/* Max length: 20 "SPT=65535 DPT=65535 " */ |
printk("SPT=%u DPT=%u LEN=%u ", |
ntohs(udph->source), ntohs(udph->dest), |
ntohs(udph->len)); |
break; |
} |
case IPPROTO_ICMPV6: { |
struct icmp6hdr *icmp6h = (struct icmp6hdr *)hdrptr; |
|
/* Max length: 13 "PROTO=ICMPv6 " */ |
printk("PROTO=ICMPv6 "); |
|
if (fragment) |
break; |
|
/* Max length: 18 "TYPE=255 CODE=255 " */ |
printk("TYPE=%u CODE=%u ", icmp6h->icmp6_type, icmp6h->icmp6_code); |
|
switch (icmp6h->icmp6_type) { |
case ICMPV6_ECHO_REQUEST: |
case ICMPV6_ECHO_REPLY: |
/* Max length: 19 "ID=65535 SEQ=65535 " */ |
printk("ID=%u SEQ=%u ", |
ntohs(icmp6h->icmp6_identifier), |
ntohs(icmp6h->icmp6_sequence)); |
break; |
case ICMPV6_MGM_QUERY: |
case ICMPV6_MGM_REPORT: |
case ICMPV6_MGM_REDUCTION: |
break; |
|
case ICMPV6_PARAMPROB: |
/* Max length: 17 "POINTER=ffffffff " */ |
printk("POINTER=%08x ", ntohl(icmp6h->icmp6_pointer)); |
/* Fall through */ |
case ICMPV6_DEST_UNREACH: |
case ICMPV6_PKT_TOOBIG: |
case ICMPV6_TIME_EXCEED: |
/* Max length: 3+maxlen */ |
if (recurse) { |
printk("["); |
dump_packet(info, (struct ipv6hdr *)(icmp6h + 1), 0); |
printk("] "); |
} |
|
/* Max length: 10 "MTU=65535 " */ |
if (icmp6h->icmp6_type == ICMPV6_PKT_TOOBIG) |
printk("MTU=%u ", ntohl(icmp6h->icmp6_mtu)); |
} |
break; |
} |
/* Max length: 10 "PROTO 255 " */ |
default: |
printk("PROTO=%u ", currenthdr); |
} |
} |
|
static unsigned int |
ip6t_log_target(struct sk_buff **pskb, |
unsigned int hooknum, |
const struct net_device *in, |
const struct net_device *out, |
const void *targinfo, |
void *userinfo) |
{ |
struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h; |
const struct ip6t_log_info *loginfo = targinfo; |
char level_string[4] = "< >"; |
|
level_string[1] = '0' + (loginfo->level % 8); |
spin_lock_bh(&log_lock); |
printk(level_string); |
printk("%sIN=%s OUT=%s ", |
loginfo->prefix, |
in ? in->name : "", |
out ? out->name : ""); |
if (in && !out) { |
/* MAC logging for input chain only. */ |
printk("MAC="); |
if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)ipv6h) { |
if ((*pskb)->dev->type != ARPHRD_SIT){ |
int i; |
unsigned char *p = (*pskb)->mac.raw; |
for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++) |
printk("%02x%c", *p, |
i==(*pskb)->dev->hard_header_len - 1 |
? ' ':':'); |
} else { |
int i; |
unsigned char *p = (*pskb)->mac.raw; |
if ( p - (ETH_ALEN*2+2) > (*pskb)->head ){ |
p -= (ETH_ALEN+2); |
for (i = 0; i < (ETH_ALEN); i++,p++) |
printk("%02x%s", *p, |
i == ETH_ALEN-1 ? "->" : ":"); |
p -= (ETH_ALEN*2); |
for (i = 0; i < (ETH_ALEN); i++,p++) |
printk("%02x%c", *p, |
i == ETH_ALEN-1 ? ' ' : ':'); |
} |
|
if (((*pskb)->dev->addr_len == 4) && |
(*pskb)->dev->hard_header_len > 20){ |
printk("TUNNEL="); |
p = (*pskb)->mac.raw + 12; |
for (i = 0; i < 4; i++,p++) |
printk("%3d%s", *p, |
i == 3 ? "->" : "."); |
for (i = 0; i < 4; i++,p++) |
printk("%3d%c", *p, |
i == 3 ? ' ' : '.'); |
} |
} |
} else |
printk(" "); |
} |
|
dump_packet(loginfo, ipv6h, 1); |
printk("\n"); |
spin_unlock_bh(&log_lock); |
|
return IP6T_CONTINUE; |
} |
|
static int ip6t_log_checkentry(const char *tablename, |
const struct ip6t_entry *e, |
void *targinfo, |
unsigned int targinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_log_info *loginfo = targinfo; |
|
if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_log_info))) { |
DEBUGP("LOG: targinfosize %u != %u\n", |
targinfosize, IP6T_ALIGN(sizeof(struct ip6t_log_info))); |
return 0; |
} |
|
if (loginfo->level >= 8) { |
DEBUGP("LOG: level %u >= 8\n", loginfo->level); |
return 0; |
} |
|
if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { |
DEBUGP("LOG: prefix term %i\n", |
loginfo->prefix[sizeof(loginfo->prefix)-1]); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_target ip6t_log_reg |
= { { NULL, NULL }, "LOG", ip6t_log_target, ip6t_log_checkentry, NULL, |
THIS_MODULE }; |
|
static int __init init(void) |
{ |
if (ip6t_register_target(&ip6t_log_reg)) |
return -EINVAL; |
|
return 0; |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_target(&ip6t_log_reg); |
} |
|
module_init(init); |
module_exit(fini); |
/ip6t_length.c
0,0 → 1,51
/* Length Match - IPv6 Port */ |
|
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/netfilter_ipv6/ip6t_length.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_length_info *info = matchinfo; |
u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); |
|
return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; |
} |
|
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_length_info))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match length_match |
= { { NULL, NULL }, "length", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&length_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&length_match); |
} |
|
module_init(init); |
module_exit(fini); |
/ip6t_rt.c
0,0 → 1,294
/* Kernel module to match ROUTING parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <asm/byteorder.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_rt.h> |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("IPv6 RT match"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
/* Returns 1 if the id is matched by the range, 0 otherwise */ |
static inline int |
segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert) |
{ |
int r=0; |
DEBUGP("rt segsleft_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ', |
min,id,max); |
r=(id >= min && id <= max) ^ invert; |
DEBUGP(" result %s\n",r? "PASS" : "FAILED"); |
return r; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct ipv6_rt_hdr *route = NULL; |
const struct ip6t_rt *rtinfo = matchinfo; |
unsigned int temp; |
unsigned int len; |
u8 nexthdr; |
unsigned int ptr; |
unsigned int hdrlen = 0; |
unsigned int ret = 0; |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
|
DEBUGP("ipv6_rt header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
break; |
} |
|
hdr=(struct ipv6_opt_hdr *)skb->data+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* ROUTING -> evaluate */ |
if (nexthdr == NEXTHDR_ROUTING) { |
temp |= MASK_ROUTING; |
break; |
} |
|
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_rt: new pointer is too large! \n"); |
break; |
} |
} |
|
/* ROUTING header not found */ |
if ( temp != MASK_ROUTING ) return 0; |
|
if (len < (int)sizeof(struct ipv6_rt_hdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
if (len < hdrlen){ |
/* Pcket smaller than its length field */ |
return 0; |
} |
|
route = (struct ipv6_rt_hdr *) (skb->data + ptr); |
|
DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen); |
DEBUGP("TYPE %04X ", route->type); |
DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left)); |
|
DEBUGP("IPv6 RT segsleft %02X ", |
(segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], |
ntohl(route->segments_left), |
!!(rtinfo->invflags & IP6T_RT_INV_SGS)))); |
DEBUGP("type %02X %02X %02X ", |
rtinfo->rt_type, route->type, |
(!(rtinfo->flags & IP6T_RT_TYP) || |
((rtinfo->rt_type == route->type) ^ |
!!(rtinfo->invflags & IP6T_RT_INV_TYP)))); |
DEBUGP("len %02X %04X %02X ", |
rtinfo->hdrlen, hdrlen, |
(!(rtinfo->flags & IP6T_RT_LEN) || |
((rtinfo->hdrlen == hdrlen) ^ |
!!(rtinfo->invflags & IP6T_RT_INV_LEN)))); |
DEBUGP("res %02X %02X %02X ", |
(rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->bitmap, |
!((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap))); |
|
ret = (route != NULL) |
&& |
(segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1], |
ntohl(route->segments_left), |
!!(rtinfo->invflags & IP6T_RT_INV_SGS))) |
&& |
(!(rtinfo->flags & IP6T_RT_LEN) || |
((rtinfo->hdrlen == hdrlen) ^ |
!!(rtinfo->invflags & IP6T_RT_INV_LEN))) |
&& |
(!(rtinfo->flags & IP6T_RT_TYP) || |
((rtinfo->rt_type == route->type) ^ |
!!(rtinfo->invflags & IP6T_RT_INV_TYP))) |
&& |
!((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap)); |
|
DEBUGP("#%d ",rtinfo->addrnr); |
temp = len = ptr = 0; |
if ( !(rtinfo->flags & IP6T_RT_FST) ){ |
return ret; |
} else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) { |
DEBUGP("Not strict "); |
if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){ |
DEBUGP("There isn't enough space\n"); |
return 0; |
} else { |
DEBUGP("#%d ",rtinfo->addrnr); |
ptr = 0; |
for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){ |
len = 0; |
while ((u8)(((struct rt0_hdr *)route)-> |
addr[temp].s6_addr[len]) == |
(u8)(rtinfo->addrs[ptr].s6_addr[len])){ |
DEBUGP("%02X?%02X ", |
(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), |
(u8)(rtinfo->addrs[ptr].s6_addr[len])); |
len++; |
if ( len == 16 ) break; |
} |
if (len==16) { |
DEBUGP("ptr=%d temp=%d;\n",ptr,temp); |
ptr++; |
} else { |
DEBUGP("%02X?%02X ", |
(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), |
(u8)(rtinfo->addrs[ptr].s6_addr[len])); |
DEBUGP("!ptr=%d temp=%d;\n",ptr,temp); |
} |
if (ptr==rtinfo->addrnr) break; |
} |
DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr); |
if ( (len == 16) && (ptr == rtinfo->addrnr)) |
return ret; |
else return 0; |
} |
} else { |
DEBUGP("Strict "); |
if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){ |
DEBUGP("There isn't enough space\n"); |
return 0; |
} else { |
DEBUGP("#%d ",rtinfo->addrnr); |
for(temp=0; temp<rtinfo->addrnr; temp++){ |
len = 0; |
while ((u8)(((struct rt0_hdr *)route)-> |
addr[temp].s6_addr[len]) == |
(u8)(rtinfo->addrs[temp].s6_addr[len])){ |
DEBUGP("%02X?%02X ", |
(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), |
(u8)(rtinfo->addrs[temp].s6_addr[len])); |
len++; |
if ( len == 16 ) break; |
} |
if (len!=16) { |
DEBUGP("%02X?%02X ", |
(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]), |
(u8)(rtinfo->addrs[temp].s6_addr[len])); |
DEBUGP("!len=%d temp=%d;\n",len,temp); |
break; |
} |
} |
DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr); |
if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16))) |
return ret; |
else return 0; |
} |
} |
|
return 0; |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_rt *rtinfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_rt))) { |
DEBUGP("ip6t_rt: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_rt))); |
return 0; |
} |
if (rtinfo->invflags & ~IP6T_RT_INV_MASK) { |
DEBUGP("ip6t_rt: unknown flags %X\n", |
rtinfo->invflags); |
return 0; |
} |
if ( (rtinfo->flags & (IP6T_RT_RES|IP6T_RT_FST_MASK)) && |
(!(rtinfo->flags & IP6T_RT_TYP) || |
(rtinfo->rt_type != 0) || |
(rtinfo->invflags & IP6T_RT_INV_TYP)) ) { |
DEBUGP("`--rt-type 0' required before `--rt-0-*'"); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match rt_match |
= { { NULL, NULL }, "rt", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&rt_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&rt_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/ip6_tables.c
0,0 → 1,1951
/* |
* Packet matching code. |
* |
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling |
* Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org> |
* |
* 19 Jan 2002 Harald Welte <laforge@gnumonks.org> |
* - increase module usage count as soon as we have rules inside |
* a table |
* 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu> |
* - new extension header parser code |
*/ |
#include <linux/config.h> |
#include <linux/skbuff.h> |
#include <linux/kmod.h> |
#include <linux/vmalloc.h> |
#include <linux/netdevice.h> |
#include <linux/module.h> |
#include <linux/tcp.h> |
#include <linux/udp.h> |
#include <linux/icmpv6.h> |
#include <net/ip.h> |
#include <asm/uaccess.h> |
#include <asm/semaphore.h> |
#include <linux/proc_fs.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
#define IPV6_HDR_LEN (sizeof(struct ipv6hdr)) |
#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr)) |
|
/*#define DEBUG_IP_FIREWALL*/ |
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */ |
/*#define DEBUG_IP_FIREWALL_USER*/ |
|
#ifdef DEBUG_IP_FIREWALL |
#define dprintf(format, args...) printk(format , ## args) |
#else |
#define dprintf(format, args...) |
#endif |
|
#ifdef DEBUG_IP_FIREWALL_USER |
#define duprintf(format, args...) printk(format , ## args) |
#else |
#define duprintf(format, args...) |
#endif |
|
#ifdef CONFIG_NETFILTER_DEBUG |
#define IP_NF_ASSERT(x) \ |
do { \ |
if (!(x)) \ |
printk("IP_NF_ASSERT: %s:%s:%u\n", \ |
__FUNCTION__, __FILE__, __LINE__); \ |
} while(0) |
#else |
#define IP_NF_ASSERT(x) |
#endif |
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) |
|
/* Mutex protects lists (only traversed in user context). */ |
static DECLARE_MUTEX(ip6t_mutex); |
|
/* Must have mutex */ |
#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0) |
#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0) |
#include <linux/netfilter_ipv4/lockhelp.h> |
#include <linux/netfilter_ipv4/listhelp.h> |
|
#if 0 |
/* All the better to debug you with... */ |
#define static |
#define inline |
#endif |
|
/* Locking is simple: we assume at worst case there will be one packet |
in user context and one from bottom halves (or soft irq if Alexey's |
softnet patch was applied). |
|
We keep a set of rules for each CPU, so we can avoid write-locking |
them; doing a readlock_bh() stops packets coming through if we're |
in user context. |
|
To be cache friendly on SMP, we arrange them like so: |
[ n-entries ] |
... cache-align padding ... |
[ n-entries ] |
|
Hence the start of any table is given by get_table() below. */ |
|
/* The table itself */ |
struct ip6t_table_info |
{ |
/* Size per table */ |
unsigned int size; |
/* Number of entries: FIXME. --RR */ |
unsigned int number; |
/* Initial number of entries. Needed for module usage count */ |
unsigned int initial_entries; |
|
/* Entry points and underflows */ |
unsigned int hook_entry[NF_IP6_NUMHOOKS]; |
unsigned int underflow[NF_IP6_NUMHOOKS]; |
|
/* ip6t_entry tables: one per CPU */ |
char entries[0] ____cacheline_aligned; |
}; |
|
static LIST_HEAD(ip6t_target); |
static LIST_HEAD(ip6t_match); |
static LIST_HEAD(ip6t_tables); |
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0) |
|
#ifdef CONFIG_SMP |
#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p)) |
#else |
#define TABLE_OFFSET(t,p) 0 |
#endif |
|
#if 0 |
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0) |
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; }) |
#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0) |
#endif |
|
static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask, |
struct in6_addr addr2) |
{ |
int i; |
for( i = 0; i < 16; i++){ |
if((addr1.s6_addr[i] & mask.s6_addr[i]) != |
(addr2.s6_addr[i] & mask.s6_addr[i])) |
return 1; |
} |
return 0; |
} |
|
/* Check for an extension */ |
int |
ip6t_ext_hdr(u8 nexthdr) |
{ |
return ( (nexthdr == IPPROTO_HOPOPTS) || |
(nexthdr == IPPROTO_ROUTING) || |
(nexthdr == IPPROTO_FRAGMENT) || |
(nexthdr == IPPROTO_ESP) || |
(nexthdr == IPPROTO_AH) || |
(nexthdr == IPPROTO_NONE) || |
(nexthdr == IPPROTO_DSTOPTS) ); |
} |
|
/* Returns whether matches rule or not. */ |
static inline int |
ip6_packet_match(const struct sk_buff *skb, |
const struct ipv6hdr *ipv6, |
const char *indev, |
const char *outdev, |
const struct ip6t_ip6 *ip6info, |
int isfrag) |
{ |
size_t i; |
unsigned long ret; |
|
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg)) |
|
if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src), |
IP6T_INV_SRCIP) |
|| FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst), |
IP6T_INV_DSTIP)) { |
dprintf("Source or dest mismatch.\n"); |
/* |
dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, |
ipinfo->smsk.s_addr, ipinfo->src.s_addr, |
ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : ""); |
dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, |
ipinfo->dmsk.s_addr, ipinfo->dst.s_addr, |
ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/ |
return 0; |
} |
|
/* Look for ifname matches; this should unroll nicely. */ |
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { |
ret |= (((const unsigned long *)indev)[i] |
^ ((const unsigned long *)ip6info->iniface)[i]) |
& ((const unsigned long *)ip6info->iniface_mask)[i]; |
} |
|
if (FWINV(ret != 0, IP6T_INV_VIA_IN)) { |
dprintf("VIA in mismatch (%s vs %s).%s\n", |
indev, ip6info->iniface, |
ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":""); |
return 0; |
} |
|
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { |
ret |= (((const unsigned long *)outdev)[i] |
^ ((const unsigned long *)ip6info->outiface)[i]) |
& ((const unsigned long *)ip6info->outiface_mask)[i]; |
} |
|
if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) { |
dprintf("VIA out mismatch (%s vs %s).%s\n", |
outdev, ip6info->outiface, |
ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":""); |
return 0; |
} |
|
/* ... might want to do something with class and flowlabel here ... */ |
|
/* look for the desired protocol header */ |
if((ip6info->flags & IP6T_F_PROTO)) { |
u_int8_t currenthdr = ipv6->nexthdr; |
struct ipv6_opt_hdr *hdrptr; |
u_int16_t ptr; /* Header offset in skb */ |
u_int16_t hdrlen; /* Header */ |
|
ptr = IPV6_HDR_LEN; |
|
while (ip6t_ext_hdr(currenthdr)) { |
/* Is there enough space for the next ext header? */ |
if (skb->len - ptr < IPV6_OPTHDR_LEN) |
return 0; |
|
/* NONE or ESP: there isn't protocol part */ |
/* If we want to count these packets in '-p all', |
* we will change the return 0 to 1*/ |
if ((currenthdr == IPPROTO_NONE) || |
(currenthdr == IPPROTO_ESP)) |
return 0; |
|
hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr); |
|
/* Size calculation */ |
if (currenthdr == IPPROTO_FRAGMENT) { |
hdrlen = 8; |
} else if (currenthdr == IPPROTO_AH) |
hdrlen = (hdrptr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdrptr); |
|
currenthdr = hdrptr->nexthdr; |
ptr += hdrlen; |
/* ptr is too large */ |
if ( ptr > skb->len ) |
return 0; |
} |
|
/* currenthdr contains the protocol header */ |
|
dprintf("Packet protocol %hi ?= %s%hi.\n", |
currenthdr, |
ip6info->invflags & IP6T_INV_PROTO ? "!":"", |
ip6info->proto); |
|
if (ip6info->proto == currenthdr) { |
if(ip6info->invflags & IP6T_INV_PROTO) { |
return 0; |
} |
return 1; |
} |
|
/* We need match for the '-p all', too! */ |
if ((ip6info->proto != 0) && |
!(ip6info->invflags & IP6T_INV_PROTO)) |
return 0; |
} |
return 1; |
} |
|
/* should be ip6 safe */ |
static inline int |
ip6_checkentry(const struct ip6t_ip6 *ipv6) |
{ |
if (ipv6->flags & ~IP6T_F_MASK) { |
duprintf("Unknown flag bits set: %08X\n", |
ipv6->flags & ~IP6T_F_MASK); |
return 0; |
} |
if (ipv6->invflags & ~IP6T_INV_MASK) { |
duprintf("Unknown invflag bits set: %08X\n", |
ipv6->invflags & ~IP6T_INV_MASK); |
return 0; |
} |
return 1; |
} |
|
static unsigned int |
ip6t_error(struct sk_buff **pskb, |
unsigned int hooknum, |
const struct net_device *in, |
const struct net_device *out, |
const void *targinfo, |
void *userinfo) |
{ |
if (net_ratelimit()) |
printk("ip6_tables: error: `%s'\n", (char *)targinfo); |
|
return NF_DROP; |
} |
|
static inline |
int do_match(struct ip6t_entry_match *m, |
const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
/* Stop iteration if it doesn't match */ |
if (!m->u.kernel.match->match(skb, in, out, m->data, |
offset, hdr, datalen, hotdrop)) |
return 1; |
else |
return 0; |
} |
|
static inline struct ip6t_entry * |
get_entry(void *base, unsigned int offset) |
{ |
return (struct ip6t_entry *)(base + offset); |
} |
|
/* Returns one of the generic firewall policies, like NF_ACCEPT. */ |
unsigned int |
ip6t_do_table(struct sk_buff **pskb, |
unsigned int hook, |
const struct net_device *in, |
const struct net_device *out, |
struct ip6t_table *table, |
void *userdata) |
{ |
static const char nulldevname[IFNAMSIZ] = { 0 }; |
u_int16_t offset = 0; |
struct ipv6hdr *ipv6; |
void *protohdr; |
u_int16_t datalen; |
int hotdrop = 0; |
/* Initializing verdict to NF_DROP keeps gcc happy. */ |
unsigned int verdict = NF_DROP; |
const char *indev, *outdev; |
void *table_base; |
struct ip6t_entry *e, *back; |
|
/* Initialization */ |
ipv6 = (*pskb)->nh.ipv6h; |
protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN); |
datalen = (*pskb)->len - IPV6_HDR_LEN; |
indev = in ? in->name : nulldevname; |
outdev = out ? out->name : nulldevname; |
|
/* We handle fragments by dealing with the first fragment as |
* if it was a normal packet. All other fragments are treated |
* normally, except that they will NEVER match rules that ask |
* things we don't know, ie. tcp syn flag or ports). If the |
* rule is also a fragment-specific rule, non-fragments won't |
* match it. */ |
|
read_lock_bh(&table->lock); |
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); |
table_base = (void *)table->private->entries |
+ TABLE_OFFSET(table->private, |
cpu_number_map(smp_processor_id())); |
e = get_entry(table_base, table->private->hook_entry[hook]); |
|
#ifdef CONFIG_NETFILTER_DEBUG |
/* Check noone else using our table */ |
if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac |
&& ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) { |
printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n", |
smp_processor_id(), |
table->name, |
&((struct ip6t_entry *)table_base)->comefrom, |
((struct ip6t_entry *)table_base)->comefrom); |
} |
((struct ip6t_entry *)table_base)->comefrom = 0x57acc001; |
#endif |
|
/* For return from builtin chain */ |
back = get_entry(table_base, table->private->underflow[hook]); |
|
do { |
IP_NF_ASSERT(e); |
IP_NF_ASSERT(back); |
(*pskb)->nfcache |= e->nfcache; |
if (ip6_packet_match(*pskb, ipv6, indev, outdev, |
&e->ipv6, offset)) { |
struct ip6t_entry_target *t; |
|
if (IP6T_MATCH_ITERATE(e, do_match, |
*pskb, in, out, |
offset, protohdr, |
datalen, &hotdrop) != 0) |
goto no_match; |
|
ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1); |
|
t = ip6t_get_target(e); |
IP_NF_ASSERT(t->u.kernel.target); |
/* Standard target? */ |
if (!t->u.kernel.target->target) { |
int v; |
|
v = ((struct ip6t_standard_target *)t)->verdict; |
if (v < 0) { |
/* Pop from stack? */ |
if (v != IP6T_RETURN) { |
verdict = (unsigned)(-v) - 1; |
break; |
} |
e = back; |
back = get_entry(table_base, |
back->comefrom); |
continue; |
} |
if (table_base + v |
!= (void *)e + e->next_offset) { |
/* Save old back ptr in next entry */ |
struct ip6t_entry *next |
= (void *)e + e->next_offset; |
next->comefrom |
= (void *)back - table_base; |
/* set back pointer to next entry */ |
back = next; |
} |
|
e = get_entry(table_base, v); |
} else { |
/* Targets which reenter must return |
abs. verdicts */ |
#ifdef CONFIG_NETFILTER_DEBUG |
((struct ip6t_entry *)table_base)->comefrom |
= 0xeeeeeeec; |
#endif |
verdict = t->u.kernel.target->target(pskb, |
hook, |
in, out, |
t->data, |
userdata); |
|
#ifdef CONFIG_NETFILTER_DEBUG |
if (((struct ip6t_entry *)table_base)->comefrom |
!= 0xeeeeeeec |
&& verdict == IP6T_CONTINUE) { |
printk("Target %s reentered!\n", |
t->u.kernel.target->name); |
verdict = NF_DROP; |
} |
((struct ip6t_entry *)table_base)->comefrom |
= 0x57acc001; |
#endif |
/* Target might have changed stuff. */ |
ipv6 = (*pskb)->nh.ipv6h; |
protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN); |
datalen = (*pskb)->len - IPV6_HDR_LEN; |
|
if (verdict == IP6T_CONTINUE) |
e = (void *)e + e->next_offset; |
else |
/* Verdict */ |
break; |
} |
} else { |
|
no_match: |
e = (void *)e + e->next_offset; |
} |
} while (!hotdrop); |
|
#ifdef CONFIG_NETFILTER_DEBUG |
((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac; |
#endif |
read_unlock_bh(&table->lock); |
|
#ifdef DEBUG_ALLOW_ALL |
return NF_ACCEPT; |
#else |
if (hotdrop) |
return NF_DROP; |
else return verdict; |
#endif |
} |
|
/* If it succeeds, returns element and locks mutex */ |
static inline void * |
find_inlist_lock_noload(struct list_head *head, |
const char *name, |
int *error, |
struct semaphore *mutex) |
{ |
void *ret; |
|
#if 1 |
duprintf("find_inlist: searching for `%s' in %s.\n", |
name, head == &ip6t_target ? "ip6t_target" |
: head == &ip6t_match ? "ip6t_match" |
: head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN"); |
#endif |
|
*error = down_interruptible(mutex); |
if (*error != 0) |
return NULL; |
|
ret = list_named_find(head, name); |
if (!ret) { |
*error = -ENOENT; |
up(mutex); |
} |
return ret; |
} |
|
#ifndef CONFIG_KMOD |
#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) |
#else |
static void * |
find_inlist_lock(struct list_head *head, |
const char *name, |
const char *prefix, |
int *error, |
struct semaphore *mutex) |
{ |
void *ret; |
|
ret = find_inlist_lock_noload(head, name, error, mutex); |
if (!ret) { |
char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1]; |
strcpy(modulename, prefix); |
strcat(modulename, name); |
duprintf("find_inlist: loading `%s'.\n", modulename); |
request_module(modulename); |
ret = find_inlist_lock_noload(head, name, error, mutex); |
} |
|
return ret; |
} |
#endif |
|
static inline struct ip6t_table * |
find_table_lock(const char *name, int *error, struct semaphore *mutex) |
{ |
return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex); |
} |
|
static inline struct ip6t_match * |
find_match_lock(const char *name, int *error, struct semaphore *mutex) |
{ |
return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex); |
} |
|
static inline struct ip6t_target * |
find_target_lock(const char *name, int *error, struct semaphore *mutex) |
{ |
return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex); |
} |
|
/* All zeroes == unconditional rule. */ |
static inline int |
unconditional(const struct ip6t_ip6 *ipv6) |
{ |
unsigned int i; |
|
for (i = 0; i < sizeof(*ipv6); i++) |
if (((char *)ipv6)[i]) |
break; |
|
return (i == sizeof(*ipv6)); |
} |
|
/* Figures out from what hook each rule can be called: returns 0 if |
there are loops. Puts hook bitmask in comefrom. */ |
static int |
mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks) |
{ |
unsigned int hook; |
|
/* No recursion; use packet counter to save back ptrs (reset |
to 0 as we leave), and comefrom to save source hook bitmask */ |
for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) { |
unsigned int pos = newinfo->hook_entry[hook]; |
struct ip6t_entry *e |
= (struct ip6t_entry *)(newinfo->entries + pos); |
|
if (!(valid_hooks & (1 << hook))) |
continue; |
|
/* Set initial back pointer. */ |
e->counters.pcnt = pos; |
|
for (;;) { |
struct ip6t_standard_target *t |
= (void *)ip6t_get_target(e); |
|
if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) { |
printk("iptables: loop hook %u pos %u %08X.\n", |
hook, pos, e->comefrom); |
return 0; |
} |
e->comefrom |
|= ((1 << hook) | (1 << NF_IP6_NUMHOOKS)); |
|
/* Unconditional return/END. */ |
if (e->target_offset == sizeof(struct ip6t_entry) |
&& (strcmp(t->target.u.user.name, |
IP6T_STANDARD_TARGET) == 0) |
&& t->verdict < 0 |
&& unconditional(&e->ipv6)) { |
unsigned int oldpos, size; |
|
/* Return: backtrack through the last |
big jump. */ |
do { |
e->comefrom ^= (1<<NF_IP6_NUMHOOKS); |
#ifdef DEBUG_IP_FIREWALL_USER |
if (e->comefrom |
& (1 << NF_IP6_NUMHOOKS)) { |
duprintf("Back unset " |
"on hook %u " |
"rule %u\n", |
hook, pos); |
} |
#endif |
oldpos = pos; |
pos = e->counters.pcnt; |
e->counters.pcnt = 0; |
|
/* We're at the start. */ |
if (pos == oldpos) |
goto next; |
|
e = (struct ip6t_entry *) |
(newinfo->entries + pos); |
} while (oldpos == pos + e->next_offset); |
|
/* Move along one */ |
size = e->next_offset; |
e = (struct ip6t_entry *) |
(newinfo->entries + pos + size); |
e->counters.pcnt = pos; |
pos += size; |
} else { |
int newpos = t->verdict; |
|
if (strcmp(t->target.u.user.name, |
IP6T_STANDARD_TARGET) == 0 |
&& newpos >= 0) { |
/* This a jump; chase it. */ |
duprintf("Jump rule %u -> %u\n", |
pos, newpos); |
} else { |
/* ... this is a fallthru */ |
newpos = pos + e->next_offset; |
} |
e = (struct ip6t_entry *) |
(newinfo->entries + newpos); |
e->counters.pcnt = pos; |
pos = newpos; |
} |
} |
next: |
duprintf("Finished chain %u\n", hook); |
} |
return 1; |
} |
|
static inline int |
cleanup_match(struct ip6t_entry_match *m, unsigned int *i) |
{ |
if (i && (*i)-- == 0) |
return 1; |
|
if (m->u.kernel.match->destroy) |
m->u.kernel.match->destroy(m->data, |
m->u.match_size - sizeof(*m)); |
|
if (m->u.kernel.match->me) |
__MOD_DEC_USE_COUNT(m->u.kernel.match->me); |
|
return 0; |
} |
|
static inline int |
standard_check(const struct ip6t_entry_target *t, |
unsigned int max_offset) |
{ |
struct ip6t_standard_target *targ = (void *)t; |
|
/* Check standard info. */ |
if (t->u.target_size |
!= IP6T_ALIGN(sizeof(struct ip6t_standard_target))) { |
duprintf("standard_check: target size %u != %u\n", |
t->u.target_size, |
IP6T_ALIGN(sizeof(struct ip6t_standard_target))); |
return 0; |
} |
|
if (targ->verdict >= 0 |
&& targ->verdict > max_offset - sizeof(struct ip6t_entry)) { |
duprintf("ip6t_standard_check: bad verdict (%i)\n", |
targ->verdict); |
return 0; |
} |
|
if (targ->verdict < -NF_MAX_VERDICT - 1) { |
duprintf("ip6t_standard_check: bad negative verdict (%i)\n", |
targ->verdict); |
return 0; |
} |
return 1; |
} |
|
static inline int |
check_match(struct ip6t_entry_match *m, |
const char *name, |
const struct ip6t_ip6 *ipv6, |
unsigned int hookmask, |
unsigned int *i) |
{ |
int ret; |
struct ip6t_match *match; |
|
match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex); |
if (!match) { |
// duprintf("check_match: `%s' not found\n", m->u.name); |
return ret; |
} |
if (match->me) |
__MOD_INC_USE_COUNT(match->me); |
m->u.kernel.match = match; |
up(&ip6t_mutex); |
|
if (m->u.kernel.match->checkentry |
&& !m->u.kernel.match->checkentry(name, ipv6, m->data, |
m->u.match_size - sizeof(*m), |
hookmask)) { |
if (m->u.kernel.match->me) |
__MOD_DEC_USE_COUNT(m->u.kernel.match->me); |
duprintf("ip_tables: check failed for `%s'.\n", |
m->u.kernel.match->name); |
return -EINVAL; |
} |
|
(*i)++; |
return 0; |
} |
|
static struct ip6t_target ip6t_standard_target; |
|
static inline int |
check_entry(struct ip6t_entry *e, const char *name, unsigned int size, |
unsigned int *i) |
{ |
struct ip6t_entry_target *t; |
struct ip6t_target *target; |
int ret; |
unsigned int j; |
|
if (!ip6_checkentry(&e->ipv6)) { |
duprintf("ip_tables: ip check failed %p %s.\n", e, name); |
return -EINVAL; |
} |
|
j = 0; |
ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); |
if (ret != 0) |
goto cleanup_matches; |
|
t = ip6t_get_target(e); |
target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex); |
if (!target) { |
duprintf("check_entry: `%s' not found\n", t->u.user.name); |
goto cleanup_matches; |
} |
if (target->me) |
__MOD_INC_USE_COUNT(target->me); |
t->u.kernel.target = target; |
up(&ip6t_mutex); |
|
if (t->u.kernel.target == &ip6t_standard_target) { |
if (!standard_check(t, size)) { |
ret = -EINVAL; |
goto cleanup_matches; |
} |
} else if (t->u.kernel.target->checkentry |
&& !t->u.kernel.target->checkentry(name, e, t->data, |
t->u.target_size |
- sizeof(*t), |
e->comefrom)) { |
if (t->u.kernel.target->me) |
__MOD_DEC_USE_COUNT(t->u.kernel.target->me); |
duprintf("ip_tables: check failed for `%s'.\n", |
t->u.kernel.target->name); |
ret = -EINVAL; |
goto cleanup_matches; |
} |
|
(*i)++; |
return 0; |
|
cleanup_matches: |
IP6T_MATCH_ITERATE(e, cleanup_match, &j); |
return ret; |
} |
|
static inline int |
check_entry_size_and_hooks(struct ip6t_entry *e, |
struct ip6t_table_info *newinfo, |
unsigned char *base, |
unsigned char *limit, |
const unsigned int *hook_entries, |
const unsigned int *underflows, |
unsigned int *i) |
{ |
unsigned int h; |
|
if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 |
|| (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) { |
duprintf("Bad offset %p\n", e); |
return -EINVAL; |
} |
|
if (e->next_offset |
< sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) { |
duprintf("checking: element %p size %u\n", |
e, e->next_offset); |
return -EINVAL; |
} |
|
/* Check hooks & underflows */ |
for (h = 0; h < NF_IP6_NUMHOOKS; h++) { |
if ((unsigned char *)e - base == hook_entries[h]) |
newinfo->hook_entry[h] = hook_entries[h]; |
if ((unsigned char *)e - base == underflows[h]) |
newinfo->underflow[h] = underflows[h]; |
} |
|
/* FIXME: underflows must be unconditional, standard verdicts |
< 0 (not IP6T_RETURN). --RR */ |
|
/* Clear counters and comefrom */ |
e->counters = ((struct ip6t_counters) { 0, 0 }); |
e->comefrom = 0; |
|
(*i)++; |
return 0; |
} |
|
static inline int |
cleanup_entry(struct ip6t_entry *e, unsigned int *i) |
{ |
struct ip6t_entry_target *t; |
|
if (i && (*i)-- == 0) |
return 1; |
|
/* Cleanup all matches */ |
IP6T_MATCH_ITERATE(e, cleanup_match, NULL); |
t = ip6t_get_target(e); |
if (t->u.kernel.target->destroy) |
t->u.kernel.target->destroy(t->data, |
t->u.target_size - sizeof(*t)); |
if (t->u.kernel.target->me) |
__MOD_DEC_USE_COUNT(t->u.kernel.target->me); |
|
return 0; |
} |
|
/* Checks and translates the user-supplied table segment (held in |
newinfo) */ |
static int |
translate_table(const char *name, |
unsigned int valid_hooks, |
struct ip6t_table_info *newinfo, |
unsigned int size, |
unsigned int number, |
const unsigned int *hook_entries, |
const unsigned int *underflows) |
{ |
unsigned int i; |
int ret; |
|
newinfo->size = size; |
newinfo->number = number; |
|
/* Init all hooks to impossible value. */ |
for (i = 0; i < NF_IP6_NUMHOOKS; i++) { |
newinfo->hook_entry[i] = 0xFFFFFFFF; |
newinfo->underflow[i] = 0xFFFFFFFF; |
} |
|
duprintf("translate_table: size %u\n", newinfo->size); |
i = 0; |
/* Walk through entries, checking offsets. */ |
ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, |
check_entry_size_and_hooks, |
newinfo, |
newinfo->entries, |
newinfo->entries + size, |
hook_entries, underflows, &i); |
if (ret != 0) |
return ret; |
|
if (i != number) { |
duprintf("translate_table: %u not %u entries\n", |
i, number); |
return -EINVAL; |
} |
|
/* Check hooks all assigned */ |
for (i = 0; i < NF_IP6_NUMHOOKS; i++) { |
/* Only hooks which are valid */ |
if (!(valid_hooks & (1 << i))) |
continue; |
if (newinfo->hook_entry[i] == 0xFFFFFFFF) { |
duprintf("Invalid hook entry %u %u\n", |
i, hook_entries[i]); |
return -EINVAL; |
} |
if (newinfo->underflow[i] == 0xFFFFFFFF) { |
duprintf("Invalid underflow %u %u\n", |
i, underflows[i]); |
return -EINVAL; |
} |
} |
|
if (!mark_source_chains(newinfo, valid_hooks)) |
return -ELOOP; |
|
/* Finally, each sanity check must pass */ |
i = 0; |
ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, |
check_entry, name, size, &i); |
|
if (ret != 0) { |
IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, |
cleanup_entry, &i); |
return ret; |
} |
|
/* And one copy for every other CPU */ |
for (i = 1; i < smp_num_cpus; i++) { |
memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i, |
newinfo->entries, |
SMP_ALIGN(newinfo->size)); |
} |
|
return ret; |
} |
|
static struct ip6t_table_info * |
replace_table(struct ip6t_table *table, |
unsigned int num_counters, |
struct ip6t_table_info *newinfo, |
int *error) |
{ |
struct ip6t_table_info *oldinfo; |
|
#ifdef CONFIG_NETFILTER_DEBUG |
{ |
struct ip6t_entry *table_base; |
unsigned int i; |
|
for (i = 0; i < smp_num_cpus; i++) { |
table_base = |
(void *)newinfo->entries |
+ TABLE_OFFSET(newinfo, i); |
|
table_base->comefrom = 0xdead57ac; |
} |
} |
#endif |
|
/* Do the substitution. */ |
write_lock_bh(&table->lock); |
/* Check inside lock: is the old number correct? */ |
if (num_counters != table->private->number) { |
duprintf("num_counters != table->private->number (%u/%u)\n", |
num_counters, table->private->number); |
write_unlock_bh(&table->lock); |
*error = -EAGAIN; |
return NULL; |
} |
oldinfo = table->private; |
table->private = newinfo; |
newinfo->initial_entries = oldinfo->initial_entries; |
write_unlock_bh(&table->lock); |
|
return oldinfo; |
} |
|
/* Gets counters. */ |
static inline int |
add_entry_to_counter(const struct ip6t_entry *e, |
struct ip6t_counters total[], |
unsigned int *i) |
{ |
ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt); |
|
(*i)++; |
return 0; |
} |
|
static void |
get_counters(const struct ip6t_table_info *t, |
struct ip6t_counters counters[]) |
{ |
unsigned int cpu; |
unsigned int i; |
|
for (cpu = 0; cpu < smp_num_cpus; cpu++) { |
i = 0; |
IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu), |
t->size, |
add_entry_to_counter, |
counters, |
&i); |
} |
} |
|
static int |
copy_entries_to_user(unsigned int total_size, |
struct ip6t_table *table, |
void *userptr) |
{ |
unsigned int off, num, countersize; |
struct ip6t_entry *e; |
struct ip6t_counters *counters; |
int ret = 0; |
|
/* We need atomic snapshot of counters: rest doesn't change |
(other than comefrom, which userspace doesn't care |
about). */ |
countersize = sizeof(struct ip6t_counters) * table->private->number; |
counters = vmalloc(countersize); |
|
if (counters == NULL) |
return -ENOMEM; |
|
/* First, sum counters... */ |
memset(counters, 0, countersize); |
write_lock_bh(&table->lock); |
get_counters(table->private, counters); |
write_unlock_bh(&table->lock); |
|
/* ... then copy entire thing from CPU 0... */ |
if (copy_to_user(userptr, table->private->entries, total_size) != 0) { |
ret = -EFAULT; |
goto free_counters; |
} |
|
/* FIXME: use iterator macros --RR */ |
/* ... then go back and fix counters and names */ |
for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){ |
unsigned int i; |
struct ip6t_entry_match *m; |
struct ip6t_entry_target *t; |
|
e = (struct ip6t_entry *)(table->private->entries + off); |
if (copy_to_user(userptr + off |
+ offsetof(struct ip6t_entry, counters), |
&counters[num], |
sizeof(counters[num])) != 0) { |
ret = -EFAULT; |
goto free_counters; |
} |
|
for (i = sizeof(struct ip6t_entry); |
i < e->target_offset; |
i += m->u.match_size) { |
m = (void *)e + i; |
|
if (copy_to_user(userptr + off + i |
+ offsetof(struct ip6t_entry_match, |
u.user.name), |
m->u.kernel.match->name, |
strlen(m->u.kernel.match->name)+1) |
!= 0) { |
ret = -EFAULT; |
goto free_counters; |
} |
} |
|
t = ip6t_get_target(e); |
if (copy_to_user(userptr + off + e->target_offset |
+ offsetof(struct ip6t_entry_target, |
u.user.name), |
t->u.kernel.target->name, |
strlen(t->u.kernel.target->name)+1) != 0) { |
ret = -EFAULT; |
goto free_counters; |
} |
} |
|
free_counters: |
vfree(counters); |
return ret; |
} |
|
static int |
get_entries(const struct ip6t_get_entries *entries, |
struct ip6t_get_entries *uptr) |
{ |
int ret; |
struct ip6t_table *t; |
|
t = find_table_lock(entries->name, &ret, &ip6t_mutex); |
if (t) { |
duprintf("t->private->number = %u\n", |
t->private->number); |
if (entries->size == t->private->size) |
ret = copy_entries_to_user(t->private->size, |
t, uptr->entrytable); |
else { |
duprintf("get_entries: I've got %u not %u!\n", |
t->private->size, |
entries->size); |
ret = -EINVAL; |
} |
up(&ip6t_mutex); |
} else |
duprintf("get_entries: Can't find %s!\n", |
entries->name); |
|
return ret; |
} |
|
static int |
do_replace(void *user, unsigned int len) |
{ |
int ret; |
struct ip6t_replace tmp; |
struct ip6t_table *t; |
struct ip6t_table_info *newinfo, *oldinfo; |
struct ip6t_counters *counters; |
|
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) |
return -EFAULT; |
|
/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ |
if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages) |
return -ENOMEM; |
|
newinfo = vmalloc(sizeof(struct ip6t_table_info) |
+ SMP_ALIGN(tmp.size) * smp_num_cpus); |
if (!newinfo) |
return -ENOMEM; |
|
if (copy_from_user(newinfo->entries, user + sizeof(tmp), |
tmp.size) != 0) { |
ret = -EFAULT; |
goto free_newinfo; |
} |
|
counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters)); |
if (!counters) { |
ret = -ENOMEM; |
goto free_newinfo; |
} |
memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters)); |
|
ret = translate_table(tmp.name, tmp.valid_hooks, |
newinfo, tmp.size, tmp.num_entries, |
tmp.hook_entry, tmp.underflow); |
if (ret != 0) |
goto free_newinfo_counters; |
|
duprintf("ip_tables: Translated table\n"); |
|
t = find_table_lock(tmp.name, &ret, &ip6t_mutex); |
if (!t) |
goto free_newinfo_counters_untrans; |
|
/* You lied! */ |
if (tmp.valid_hooks != t->valid_hooks) { |
duprintf("Valid hook crap: %08X vs %08X\n", |
tmp.valid_hooks, t->valid_hooks); |
ret = -EINVAL; |
goto free_newinfo_counters_untrans_unlock; |
} |
|
oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); |
if (!oldinfo) |
goto free_newinfo_counters_untrans_unlock; |
|
/* Update module usage count based on number of rules */ |
duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n", |
oldinfo->number, oldinfo->initial_entries, newinfo->number); |
if (t->me && (oldinfo->number <= oldinfo->initial_entries) && |
(newinfo->number > oldinfo->initial_entries)) |
__MOD_INC_USE_COUNT(t->me); |
else if (t->me && (oldinfo->number > oldinfo->initial_entries) && |
(newinfo->number <= oldinfo->initial_entries)) |
__MOD_DEC_USE_COUNT(t->me); |
|
/* Get the old counters. */ |
get_counters(oldinfo, counters); |
/* Decrease module usage counts and free resource */ |
IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL); |
vfree(oldinfo); |
/* Silent error: too late now. */ |
copy_to_user(tmp.counters, counters, |
sizeof(struct ip6t_counters) * tmp.num_counters); |
vfree(counters); |
up(&ip6t_mutex); |
return 0; |
|
free_newinfo_counters_untrans_unlock: |
up(&ip6t_mutex); |
free_newinfo_counters_untrans: |
IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL); |
free_newinfo_counters: |
vfree(counters); |
free_newinfo: |
vfree(newinfo); |
return ret; |
} |
|
/* We're lazy, and add to the first CPU; overflow works its fey magic |
* and everything is OK. */ |
static inline int |
add_counter_to_entry(struct ip6t_entry *e, |
const struct ip6t_counters addme[], |
unsigned int *i) |
{ |
#if 0 |
duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n", |
*i, |
(long unsigned int)e->counters.pcnt, |
(long unsigned int)e->counters.bcnt, |
(long unsigned int)addme[*i].pcnt, |
(long unsigned int)addme[*i].bcnt); |
#endif |
|
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); |
|
(*i)++; |
return 0; |
} |
|
static int |
do_add_counters(void *user, unsigned int len) |
{ |
unsigned int i; |
struct ip6t_counters_info tmp, *paddc; |
struct ip6t_table *t; |
int ret; |
|
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) |
return -EFAULT; |
|
if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters)) |
return -EINVAL; |
|
paddc = vmalloc(len); |
if (!paddc) |
return -ENOMEM; |
|
if (copy_from_user(paddc, user, len) != 0) { |
ret = -EFAULT; |
goto free; |
} |
|
t = find_table_lock(tmp.name, &ret, &ip6t_mutex); |
if (!t) |
goto free; |
|
write_lock_bh(&t->lock); |
if (t->private->number != paddc->num_counters) { |
ret = -EINVAL; |
goto unlock_up_free; |
} |
|
i = 0; |
IP6T_ENTRY_ITERATE(t->private->entries, |
t->private->size, |
add_counter_to_entry, |
paddc->counters, |
&i); |
unlock_up_free: |
write_unlock_bh(&t->lock); |
up(&ip6t_mutex); |
free: |
vfree(paddc); |
|
return ret; |
} |
|
static int |
do_ip6t_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len) |
{ |
int ret; |
|
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
|
switch (cmd) { |
case IP6T_SO_SET_REPLACE: |
ret = do_replace(user, len); |
break; |
|
case IP6T_SO_SET_ADD_COUNTERS: |
ret = do_add_counters(user, len); |
break; |
|
default: |
duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd); |
ret = -EINVAL; |
} |
|
return ret; |
} |
|
static int |
do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len) |
{ |
int ret; |
|
if (!capable(CAP_NET_ADMIN)) |
return -EPERM; |
|
switch (cmd) { |
case IP6T_SO_GET_INFO: { |
char name[IP6T_TABLE_MAXNAMELEN]; |
struct ip6t_table *t; |
|
if (*len != sizeof(struct ip6t_getinfo)) { |
duprintf("length %u != %u\n", *len, |
sizeof(struct ip6t_getinfo)); |
ret = -EINVAL; |
break; |
} |
|
if (copy_from_user(name, user, sizeof(name)) != 0) { |
ret = -EFAULT; |
break; |
} |
name[IP6T_TABLE_MAXNAMELEN-1] = '\0'; |
t = find_table_lock(name, &ret, &ip6t_mutex); |
if (t) { |
struct ip6t_getinfo info; |
|
info.valid_hooks = t->valid_hooks; |
memcpy(info.hook_entry, t->private->hook_entry, |
sizeof(info.hook_entry)); |
memcpy(info.underflow, t->private->underflow, |
sizeof(info.underflow)); |
info.num_entries = t->private->number; |
info.size = t->private->size; |
strcpy(info.name, name); |
|
if (copy_to_user(user, &info, *len) != 0) |
ret = -EFAULT; |
else |
ret = 0; |
|
up(&ip6t_mutex); |
} |
} |
break; |
|
case IP6T_SO_GET_ENTRIES: { |
struct ip6t_get_entries get; |
|
if (*len < sizeof(get)) { |
duprintf("get_entries: %u < %u\n", *len, sizeof(get)); |
ret = -EINVAL; |
} else if (copy_from_user(&get, user, sizeof(get)) != 0) { |
ret = -EFAULT; |
} else if (*len != sizeof(struct ip6t_get_entries) + get.size) { |
duprintf("get_entries: %u != %u\n", *len, |
sizeof(struct ip6t_get_entries) + get.size); |
ret = -EINVAL; |
} else |
ret = get_entries(&get, user); |
break; |
} |
|
default: |
duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd); |
ret = -EINVAL; |
} |
|
return ret; |
} |
|
/* Registration hooks for targets. */ |
int |
ip6t_register_target(struct ip6t_target *target) |
{ |
int ret; |
|
MOD_INC_USE_COUNT; |
ret = down_interruptible(&ip6t_mutex); |
if (ret != 0) { |
MOD_DEC_USE_COUNT; |
return ret; |
} |
if (!list_named_insert(&ip6t_target, target)) { |
duprintf("ip6t_register_target: `%s' already in list!\n", |
target->name); |
ret = -EINVAL; |
MOD_DEC_USE_COUNT; |
} |
up(&ip6t_mutex); |
return ret; |
} |
|
void |
ip6t_unregister_target(struct ip6t_target *target) |
{ |
down(&ip6t_mutex); |
LIST_DELETE(&ip6t_target, target); |
up(&ip6t_mutex); |
MOD_DEC_USE_COUNT; |
} |
|
int |
ip6t_register_match(struct ip6t_match *match) |
{ |
int ret; |
|
MOD_INC_USE_COUNT; |
ret = down_interruptible(&ip6t_mutex); |
if (ret != 0) { |
MOD_DEC_USE_COUNT; |
return ret; |
} |
if (!list_named_insert(&ip6t_match, match)) { |
duprintf("ip6t_register_match: `%s' already in list!\n", |
match->name); |
MOD_DEC_USE_COUNT; |
ret = -EINVAL; |
} |
up(&ip6t_mutex); |
|
return ret; |
} |
|
void |
ip6t_unregister_match(struct ip6t_match *match) |
{ |
down(&ip6t_mutex); |
LIST_DELETE(&ip6t_match, match); |
up(&ip6t_mutex); |
MOD_DEC_USE_COUNT; |
} |
|
int ip6t_register_table(struct ip6t_table *table) |
{ |
int ret; |
struct ip6t_table_info *newinfo; |
static struct ip6t_table_info bootstrap |
= { 0, 0, 0, { 0 }, { 0 }, { } }; |
|
MOD_INC_USE_COUNT; |
newinfo = vmalloc(sizeof(struct ip6t_table_info) |
+ SMP_ALIGN(table->table->size) * smp_num_cpus); |
if (!newinfo) { |
ret = -ENOMEM; |
MOD_DEC_USE_COUNT; |
return ret; |
} |
memcpy(newinfo->entries, table->table->entries, table->table->size); |
|
ret = translate_table(table->name, table->valid_hooks, |
newinfo, table->table->size, |
table->table->num_entries, |
table->table->hook_entry, |
table->table->underflow); |
if (ret != 0) { |
vfree(newinfo); |
MOD_DEC_USE_COUNT; |
return ret; |
} |
|
ret = down_interruptible(&ip6t_mutex); |
if (ret != 0) { |
vfree(newinfo); |
MOD_DEC_USE_COUNT; |
return ret; |
} |
|
/* Don't autoload: we'd eat our tail... */ |
if (list_named_find(&ip6t_tables, table->name)) { |
ret = -EEXIST; |
goto free_unlock; |
} |
|
/* Simplifies replace_table code. */ |
table->private = &bootstrap; |
if (!replace_table(table, 0, newinfo, &ret)) |
goto free_unlock; |
|
duprintf("table->private->number = %u\n", |
table->private->number); |
|
/* save number of initial entries */ |
table->private->initial_entries = table->private->number; |
|
table->lock = RW_LOCK_UNLOCKED; |
list_prepend(&ip6t_tables, table); |
|
unlock: |
up(&ip6t_mutex); |
return ret; |
|
free_unlock: |
vfree(newinfo); |
MOD_DEC_USE_COUNT; |
goto unlock; |
} |
|
void ip6t_unregister_table(struct ip6t_table *table) |
{ |
down(&ip6t_mutex); |
LIST_DELETE(&ip6t_tables, table); |
up(&ip6t_mutex); |
|
/* Decrease module usage counts and free resources */ |
IP6T_ENTRY_ITERATE(table->private->entries, table->private->size, |
cleanup_entry, NULL); |
vfree(table->private); |
MOD_DEC_USE_COUNT; |
} |
|
/* Returns 1 if the port is matched by the range, 0 otherwise */ |
static inline int |
port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert) |
{ |
int ret; |
|
ret = (port >= min && port <= max) ^ invert; |
return ret; |
} |
|
static int |
tcp_find_option(u_int8_t option, |
const struct tcphdr *tcp, |
u_int16_t datalen, |
int invert, |
int *hotdrop) |
{ |
unsigned int i = sizeof(struct tcphdr); |
const u_int8_t *opt = (u_int8_t *)tcp; |
|
duprintf("tcp_match: finding option\n"); |
/* If we don't have the whole header, drop packet. */ |
if (tcp->doff * 4 < sizeof(struct tcphdr) || |
tcp->doff * 4 > datalen) { |
*hotdrop = 1; |
return 0; |
} |
|
while (i < tcp->doff * 4) { |
if (opt[i] == option) return !invert; |
if (opt[i] < 2) i++; |
else i += opt[i+1]?:1; |
} |
|
return invert; |
} |
|
static int |
tcp_match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct tcphdr *tcp; |
const struct ip6t_tcp *tcpinfo = matchinfo; |
int tcpoff; |
u8 nexthdr = skb->nh.ipv6h->nexthdr; |
|
/* To quote Alan: |
|
Don't allow a fragment of TCP 8 bytes in. Nobody normal |
causes this. Its a cracker trying to break in by doing a |
flag overwrite to pass the direction checks. |
*/ |
|
if (offset == 1) { |
duprintf("Dropping evil TCP offset=1 frag.\n"); |
*hotdrop = 1; |
return 0; |
} else if (offset == 0 && datalen < sizeof(struct tcphdr)) { |
/* We've been asked to examine this packet, and we |
can't. Hence, no choice but to drop. */ |
duprintf("Dropping evil TCP offset=0 tinygram.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data; |
tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff); |
if (tcpoff < 0 || tcpoff > skb->len) { |
duprintf("tcp_match: cannot skip exthdr. Dropping.\n"); |
*hotdrop = 1; |
return 0; |
} else if (nexthdr == IPPROTO_FRAGMENT) |
return 0; |
else if (nexthdr != IPPROTO_TCP || |
skb->len - tcpoff < sizeof(struct tcphdr)) { |
/* cannot be occured */ |
duprintf("tcp_match: cannot get TCP header. Dropping.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
tcp = (struct tcphdr *)(skb->data + tcpoff); |
|
/* FIXME: Try tcp doff >> packet len against various stacks --RR */ |
|
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg)) |
|
/* Must not be a fragment. */ |
return !offset |
&& port_match(tcpinfo->spts[0], tcpinfo->spts[1], |
ntohs(tcp->source), |
!!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)) |
&& port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], |
ntohs(tcp->dest), |
!!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)) |
&& FWINVTCP((((unsigned char *)tcp)[13] |
& tcpinfo->flg_mask) |
== tcpinfo->flg_cmp, |
IP6T_TCP_INV_FLAGS) |
&& (!tcpinfo->option |
|| tcp_find_option(tcpinfo->option, tcp, datalen, |
tcpinfo->invflags |
& IP6T_TCP_INV_OPTION, |
hotdrop)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
tcp_checkentry(const char *tablename, |
const struct ip6t_ip6 *ipv6, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
const struct ip6t_tcp *tcpinfo = matchinfo; |
|
/* Must specify proto == TCP, and no unknown invflags */ |
return ipv6->proto == IPPROTO_TCP |
&& !(ipv6->invflags & IP6T_INV_PROTO) |
&& matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp)) |
&& !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK); |
} |
|
static int |
udp_match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct udphdr *udp; |
const struct ip6t_udp *udpinfo = matchinfo; |
int udpoff; |
u8 nexthdr = skb->nh.ipv6h->nexthdr; |
|
if (offset == 0 && datalen < sizeof(struct udphdr)) { |
/* We've been asked to examine this packet, and we |
can't. Hence, no choice but to drop. */ |
duprintf("Dropping evil UDP tinygram.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data; |
udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff); |
if (udpoff < 0 || udpoff > skb->len) { |
duprintf("udp_match: cannot skip exthdr. Dropping.\n"); |
*hotdrop = 1; |
return 0; |
} else if (nexthdr == IPPROTO_FRAGMENT) |
return 0; |
else if (nexthdr != IPPROTO_UDP || |
skb->len - udpoff < sizeof(struct udphdr)) { |
duprintf("udp_match: cannot get UDP header. Dropping.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
udp = (struct udphdr *)(skb->data + udpoff); |
|
/* Must not be a fragment. */ |
return !offset |
&& port_match(udpinfo->spts[0], udpinfo->spts[1], |
ntohs(udp->source), |
!!(udpinfo->invflags & IP6T_UDP_INV_SRCPT)) |
&& port_match(udpinfo->dpts[0], udpinfo->dpts[1], |
ntohs(udp->dest), |
!!(udpinfo->invflags & IP6T_UDP_INV_DSTPT)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
udp_checkentry(const char *tablename, |
const struct ip6t_ip6 *ipv6, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_udp *udpinfo = matchinfo; |
|
/* Must specify proto == UDP, and no unknown invflags */ |
if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) { |
duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto, |
IPPROTO_UDP); |
return 0; |
} |
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) { |
duprintf("ip6t_udp: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp))); |
return 0; |
} |
if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) { |
duprintf("ip6t_udp: unknown flags %X\n", |
udpinfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
/* Returns 1 if the type and code is matched by the range, 0 otherwise */ |
static inline int |
icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code, |
u_int8_t type, u_int8_t code, |
int invert) |
{ |
return (type == test_type && code >= min_code && code <= max_code) |
^ invert; |
} |
|
static int |
icmp6_match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct icmp6hdr *icmp = hdr; |
const struct ip6t_icmp *icmpinfo = matchinfo; |
|
if (offset == 0 && datalen < 2) { |
/* We've been asked to examine this packet, and we |
can't. Hence, no choice but to drop. */ |
duprintf("Dropping evil ICMP tinygram.\n"); |
*hotdrop = 1; |
return 0; |
} |
|
/* Must not be a fragment. */ |
return !offset |
&& icmp6_type_code_match(icmpinfo->type, |
icmpinfo->code[0], |
icmpinfo->code[1], |
icmp->icmp6_type, icmp->icmp6_code, |
!!(icmpinfo->invflags&IP6T_ICMP_INV)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
icmp6_checkentry(const char *tablename, |
const struct ip6t_ip6 *ipv6, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
const struct ip6t_icmp *icmpinfo = matchinfo; |
|
/* Must specify proto == ICMP, and no unknown invflags */ |
return ipv6->proto == IPPROTO_ICMPV6 |
&& !(ipv6->invflags & IP6T_INV_PROTO) |
&& matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp)) |
&& !(icmpinfo->invflags & ~IP6T_ICMP_INV); |
} |
|
/* The built-in targets: standard (NULL) and error. */ |
static struct ip6t_target ip6t_standard_target |
= { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL }; |
static struct ip6t_target ip6t_error_target |
= { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL }; |
|
static struct nf_sockopt_ops ip6t_sockopts |
= { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl, |
IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL }; |
|
static struct ip6t_match tcp_matchstruct |
= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL }; |
static struct ip6t_match udp_matchstruct |
= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL }; |
static struct ip6t_match icmp6_matchstruct |
= { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL }; |
|
#ifdef CONFIG_PROC_FS |
static inline int print_name(const char *i, |
off_t start_offset, char *buffer, int length, |
off_t *pos, unsigned int *count) |
{ |
if ((*count)++ >= start_offset) { |
unsigned int namelen; |
|
namelen = sprintf(buffer + *pos, "%s\n", |
i + sizeof(struct list_head)); |
if (*pos + namelen > length) { |
/* Stop iterating */ |
return 1; |
} |
*pos += namelen; |
} |
return 0; |
} |
|
static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length) |
{ |
off_t pos = 0; |
unsigned int count = 0; |
|
if (down_interruptible(&ip6t_mutex) != 0) |
return 0; |
|
LIST_FIND(&ip6t_tables, print_name, char *, |
offset, buffer, length, &pos, &count); |
|
up(&ip6t_mutex); |
|
/* `start' hack - see fs/proc/generic.c line ~105 */ |
*start=(char *)((unsigned long)count-offset); |
return pos; |
} |
|
static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length) |
{ |
off_t pos = 0; |
unsigned int count = 0; |
|
if (down_interruptible(&ip6t_mutex) != 0) |
return 0; |
|
LIST_FIND(&ip6t_target, print_name, char *, |
offset, buffer, length, &pos, &count); |
|
up(&ip6t_mutex); |
|
*start = (char *)((unsigned long)count - offset); |
return pos; |
} |
|
static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length) |
{ |
off_t pos = 0; |
unsigned int count = 0; |
|
if (down_interruptible(&ip6t_mutex) != 0) |
return 0; |
|
LIST_FIND(&ip6t_match, print_name, char *, |
offset, buffer, length, &pos, &count); |
|
up(&ip6t_mutex); |
|
*start = (char *)((unsigned long)count - offset); |
return pos; |
} |
|
static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] = |
{ { "ip6_tables_names", ip6t_get_tables }, |
{ "ip6_tables_targets", ip6t_get_targets }, |
{ "ip6_tables_matches", ip6t_get_matches }, |
{ NULL, NULL} }; |
#endif /*CONFIG_PROC_FS*/ |
|
static int __init init(void) |
{ |
int ret; |
|
/* Noone else will be downing sem now, so we won't sleep */ |
down(&ip6t_mutex); |
list_append(&ip6t_target, &ip6t_standard_target); |
list_append(&ip6t_target, &ip6t_error_target); |
list_append(&ip6t_match, &tcp_matchstruct); |
list_append(&ip6t_match, &udp_matchstruct); |
list_append(&ip6t_match, &icmp6_matchstruct); |
up(&ip6t_mutex); |
|
/* Register setsockopt */ |
ret = nf_register_sockopt(&ip6t_sockopts); |
if (ret < 0) { |
duprintf("Unable to register sockopts.\n"); |
return ret; |
} |
|
#ifdef CONFIG_PROC_FS |
{ |
struct proc_dir_entry *proc; |
int i; |
|
for (i = 0; ip6t_proc_entry[i].name; i++) { |
proc = proc_net_create(ip6t_proc_entry[i].name, 0, |
ip6t_proc_entry[i].get_info); |
if (!proc) { |
while (--i >= 0) |
proc_net_remove(ip6t_proc_entry[i].name); |
nf_unregister_sockopt(&ip6t_sockopts); |
return -ENOMEM; |
} |
proc->owner = THIS_MODULE; |
} |
} |
#endif |
|
printk("ip6_tables: (C) 2000-2002 Netfilter core team\n"); |
return 0; |
} |
|
static void __exit fini(void) |
{ |
nf_unregister_sockopt(&ip6t_sockopts); |
#ifdef CONFIG_PROC_FS |
{ |
int i; |
for (i = 0; ip6t_proc_entry[i].name; i++) |
proc_net_remove(ip6t_proc_entry[i].name); |
} |
#endif |
} |
|
EXPORT_SYMBOL(ip6t_register_table); |
EXPORT_SYMBOL(ip6t_unregister_table); |
EXPORT_SYMBOL(ip6t_do_table); |
EXPORT_SYMBOL(ip6t_register_match); |
EXPORT_SYMBOL(ip6t_unregister_match); |
EXPORT_SYMBOL(ip6t_register_target); |
EXPORT_SYMBOL(ip6t_unregister_target); |
EXPORT_SYMBOL(ip6t_ext_hdr); |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
/ip6table_mangle.c
0,0 → 1,244
/* |
* IPv6 packet mangling table, a port of the IPv4 mangle table to IPv6 |
* |
* Copyright (C) 2000-2001 by Harald Welte <laforge@gnumonks.org> |
*/ |
#include <linux/module.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
#define MANGLE_VALID_HOOKS ((1 << NF_IP6_PRE_ROUTING) | \ |
(1 << NF_IP6_LOCAL_IN) | \ |
(1 << NF_IP6_FORWARD) | \ |
(1 << NF_IP6_LOCAL_OUT) | \ |
(1 << NF_IP6_POST_ROUTING)) |
|
#if 0 |
#define DEBUGP(x, args...) printk(KERN_DEBUG x, ## args) |
#else |
#define DEBUGP(x, args...) |
#endif |
|
/* Standard entry. */ |
struct ip6t_standard |
{ |
struct ip6t_entry entry; |
struct ip6t_standard_target target; |
}; |
|
struct ip6t_error_target |
{ |
struct ip6t_entry_target target; |
char errorname[IP6T_FUNCTION_MAXNAMELEN]; |
}; |
|
struct ip6t_error |
{ |
struct ip6t_entry entry; |
struct ip6t_error_target target; |
}; |
|
static struct |
{ |
struct ip6t_replace repl; |
struct ip6t_standard entries[5]; |
struct ip6t_error term; |
} initial_table __initdata |
= { { "mangle", MANGLE_VALID_HOOKS, 6, |
sizeof(struct ip6t_standard) * 5 + sizeof(struct ip6t_error), |
{ [NF_IP6_PRE_ROUTING] 0, |
[NF_IP6_LOCAL_IN] sizeof(struct ip6t_standard), |
[NF_IP6_FORWARD] sizeof(struct ip6t_standard) * 2, |
[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 3, |
[NF_IP6_POST_ROUTING] sizeof(struct ip6t_standard) * 4}, |
{ [NF_IP6_PRE_ROUTING] 0, |
[NF_IP6_LOCAL_IN] sizeof(struct ip6t_standard), |
[NF_IP6_FORWARD] sizeof(struct ip6t_standard) * 2, |
[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) * 3, |
[NF_IP6_POST_ROUTING] sizeof(struct ip6t_standard) * 4}, |
0, NULL, { } }, |
{ |
/* PRE_ROUTING */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* LOCAL_IN */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* FORWARD */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* LOCAL_OUT */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } }, |
/* POST_ROUTING */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_standard), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, |
-NF_ACCEPT - 1 } } |
}, |
/* ERROR */ |
{ { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, |
0, |
sizeof(struct ip6t_entry), |
sizeof(struct ip6t_error), |
0, { 0, 0 }, { } }, |
{ { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, |
{ } }, |
"ERROR" |
} |
} |
}; |
|
static struct ip6t_table packet_mangler |
= { { NULL, NULL }, "mangle", &initial_table.repl, |
MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE }; |
|
/* The work comes in here from netfilter.c. */ |
static unsigned int |
ip6t_route_hook(unsigned int hook, |
struct sk_buff **pskb, |
const struct net_device *in, |
const struct net_device *out, |
int (*okfn)(struct sk_buff *)) |
{ |
return ip6t_do_table(pskb, hook, in, out, &packet_mangler, NULL); |
} |
|
static unsigned int |
ip6t_local_hook(unsigned int hook, |
struct sk_buff **pskb, |
const struct net_device *in, |
const struct net_device *out, |
int (*okfn)(struct sk_buff *)) |
{ |
|
unsigned long nfmark; |
unsigned int ret; |
struct in6_addr saddr, daddr; |
u_int8_t hop_limit; |
u_int32_t flowlabel; |
|
#if 0 |
/* root is playing with raw sockets. */ |
if ((*pskb)->len < sizeof(struct iphdr) |
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) { |
if (net_ratelimit()) |
printk("ip6t_hook: happy cracking.\n"); |
return NF_ACCEPT; |
} |
#endif |
|
/* save source/dest address, nfmark, hoplimit, flowlabel, priority, */ |
memcpy(&saddr, &(*pskb)->nh.ipv6h->saddr, sizeof(saddr)); |
memcpy(&daddr, &(*pskb)->nh.ipv6h->daddr, sizeof(daddr)); |
nfmark = (*pskb)->nfmark; |
hop_limit = (*pskb)->nh.ipv6h->hop_limit; |
|
/* flowlabel and prio (includes version, which shouldn't change either */ |
flowlabel = *((u_int32_t *) (*pskb)->nh.ipv6h); |
|
ret = ip6t_do_table(pskb, hook, in, out, &packet_mangler, NULL); |
|
if (ret != NF_DROP && ret != NF_STOLEN |
&& (memcmp(&(*pskb)->nh.ipv6h->saddr, &saddr, sizeof(saddr)) |
|| memcmp(&(*pskb)->nh.ipv6h->daddr, &daddr, sizeof(daddr)) |
|| (*pskb)->nfmark != nfmark |
|| (*pskb)->nh.ipv6h->hop_limit != hop_limit)) { |
|
/* something which could affect routing has changed */ |
|
DEBUGP("ip6table_mangle: we'd need to re-route a packet\n"); |
} |
|
return ret; |
} |
|
static struct nf_hook_ops ip6t_ops[] |
= { { { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_PRE_ROUTING, NF_IP6_PRI_MANGLE }, |
{ { NULL, NULL }, ip6t_local_hook, PF_INET6, NF_IP6_LOCAL_IN, NF_IP6_PRI_MANGLE }, |
{ { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_FORWARD, NF_IP6_PRI_MANGLE }, |
{ { NULL, NULL }, ip6t_local_hook, PF_INET6, NF_IP6_LOCAL_OUT, NF_IP6_PRI_MANGLE }, |
{ { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_POST_ROUTING, NF_IP6_PRI_MANGLE } |
}; |
|
static int __init init(void) |
{ |
int ret; |
|
/* Register table */ |
ret = ip6t_register_table(&packet_mangler); |
if (ret < 0) |
return ret; |
|
/* Register hooks */ |
ret = nf_register_hook(&ip6t_ops[0]); |
if (ret < 0) |
goto cleanup_table; |
|
ret = nf_register_hook(&ip6t_ops[1]); |
if (ret < 0) |
goto cleanup_hook0; |
|
ret = nf_register_hook(&ip6t_ops[2]); |
if (ret < 0) |
goto cleanup_hook1; |
|
ret = nf_register_hook(&ip6t_ops[3]); |
if (ret < 0) |
goto cleanup_hook2; |
|
ret = nf_register_hook(&ip6t_ops[4]); |
if (ret < 0) |
goto cleanup_hook3; |
|
return ret; |
|
cleanup_hook3: |
nf_unregister_hook(&ip6t_ops[3]); |
cleanup_hook2: |
nf_unregister_hook(&ip6t_ops[2]); |
cleanup_hook1: |
nf_unregister_hook(&ip6t_ops[1]); |
cleanup_hook0: |
nf_unregister_hook(&ip6t_ops[0]); |
cleanup_table: |
ip6t_unregister_table(&packet_mangler); |
|
return ret; |
} |
|
static void __exit fini(void) |
{ |
unsigned int i; |
|
for (i = 0; i < sizeof(ip6t_ops)/sizeof(struct nf_hook_ops); i++) |
nf_unregister_hook(&ip6t_ops[i]); |
|
ip6t_unregister_table(&packet_mangler); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
/ip6t_esp.c
0,0 → 1,175
/* Kernel module to match ESP parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_esp.h> |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("IPv6 ESP match"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
struct esphdr { |
__u32 spi; |
}; |
|
/* Returns 1 if the spi is matched by the range, 0 otherwise */ |
static inline int |
spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert) |
{ |
int r=0; |
DEBUGP("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ', |
min,spi,max); |
r=(spi >= min && spi <= max) ^ invert; |
DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n"); |
return r; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct esphdr *esp = NULL; |
const struct ip6t_esp *espinfo = matchinfo; |
unsigned int temp; |
int len; |
u8 nexthdr; |
unsigned int ptr; |
|
/* Make sure this isn't an evil packet */ |
/*DEBUGP("ipv6_esp entered \n");*/ |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
int hdrlen; |
|
DEBUGP("ipv6_esp header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
temp |= MASK_ESP; |
break; |
} |
|
hdr=(struct ipv6_opt_hdr *)skb->data+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_esp: new pointer too large! \n"); |
break; |
} |
} |
|
/* ESP header not found */ |
if ( temp != MASK_ESP ) return 0; |
|
if (len < (int)sizeof(struct esphdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
esp = (struct esphdr *) (skb->data + ptr); |
|
DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(esp->spi), ntohl(esp->spi)); |
|
return (esp != NULL) |
&& spi_match(espinfo->spis[0], espinfo->spis[1], |
ntohl(esp->spi), |
!!(espinfo->invflags & IP6T_ESP_INV_SPI)); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_esp *espinfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_esp))) { |
DEBUGP("ip6t_esp: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_esp))); |
return 0; |
} |
if (espinfo->invflags & ~IP6T_ESP_INV_MASK) { |
DEBUGP("ip6t_esp: unknown flags %X\n", |
espinfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match esp_match |
= { { NULL, NULL }, "esp", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&esp_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&esp_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/Config.in
0,0 → 1,78
# |
# IP netfilter configuration |
# |
mainmenu_option next_comment |
comment ' IPv6: Netfilter Configuration' |
|
#tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP6_NF_CONNTRACK |
#if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then |
# dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK |
#fi |
|
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
tristate 'Userspace queueing via NETLINK (EXPERIMENTAL)' CONFIG_IP6_NF_QUEUE |
fi |
|
tristate 'IP6 tables support (required for filtering/masq/NAT)' CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then |
# The simple matches. |
dep_tristate ' limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES |
dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' Routing header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_RT $CONFIG_IP6_NF_IPTABLES |
fi |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' Hop-by-Hop and Dst opts header match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OPTS $CONFIG_IP6_NF_IPTABLES |
fi |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' Fragmentation header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_FRAG $CONFIG_IP6_NF_IPTABLES |
fi |
dep_tristate ' HL match support' CONFIG_IP6_NF_MATCH_HL $CONFIG_IP6_NF_IPTABLES |
dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OWNER $CONFIG_IP6_NF_IPTABLES |
fi |
# dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES |
dep_tristate ' netfilter MARK match support' CONFIG_IP6_NF_MATCH_MARK $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' IPv6 Extension Headers Match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_IPV6HEADER $CONFIG_IP6_NF_IPTABLES |
fi |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' AH/ESP match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_AHESP $CONFIG_IP6_NF_IPTABLES |
fi |
dep_tristate ' Packet Length match support' CONFIG_IP6_NF_MATCH_LENGTH $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
dep_tristate ' EUI64 address check (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_EUI64 $CONFIG_IP6_NF_IPTABLES |
fi |
# dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES |
# dep_tristate ' TOS match support' CONFIG_IP6_NF_MATCH_TOS $CONFIG_IP6_NF_IPTABLES |
# if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then |
# dep_tristate ' Connection state match support' CONFIG_IP6_NF_MATCH_STATE $CONFIG_IP6_NF_CONNTRACK $CONFIG_IP6_NF_IPTABLES |
# fi |
# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
# dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_UNCLEAN $CONFIG_IP6_NF_IPTABLES |
# dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OWNER $CONFIG_IP6_NF_IPTABLES |
# fi |
|
# The targets |
dep_tristate ' Packet filtering' CONFIG_IP6_NF_FILTER $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then |
dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_FILTER |
fi |
|
# if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then |
# dep_tristate ' REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER |
# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
# dep_tristate ' MIRROR target support (EXPERIMENTAL)' CONFIG_IP6_NF_TARGET_MIRROR $CONFIG_IP6_NF_FILTER |
# fi |
# fi |
|
dep_tristate ' Packet mangling' CONFIG_IP6_NF_MANGLE $CONFIG_IP6_NF_IPTABLES |
if [ "$CONFIG_IP6_NF_MANGLE" != "n" ]; then |
# dep_tristate ' TOS target support' CONFIG_IP6_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE |
dep_tristate ' MARK target support' CONFIG_IP6_NF_TARGET_MARK $CONFIG_IP6_NF_MANGLE |
fi |
#dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES |
fi |
|
endmenu |
/ip6t_ah.c
0,0 → 1,207
/* Kernel module to match AH parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_ah.h> |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
MODULE_DESCRIPTION("IPv6 AH match"); |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
struct ahhdr { |
__u8 nexthdr; |
__u8 hdrlen; |
__u16 reserved; |
__u32 spi; |
}; |
|
/* Returns 1 if the spi is matched by the range, 0 otherwise */ |
static inline int |
spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert) |
{ |
int r=0; |
DEBUGP("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ', |
min,spi,max); |
r=(spi >= min && spi <= max) ^ invert; |
DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n"); |
return r; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct ahhdr *ah = NULL; |
const struct ip6t_ah *ahinfo = matchinfo; |
unsigned int temp; |
int len; |
u8 nexthdr; |
unsigned int ptr; |
unsigned int hdrlen = 0; |
|
/*DEBUGP("IPv6 AH entered\n");*/ |
/* if (opt->auth == 0) return 0; |
* It does not filled on output */ |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
|
DEBUGP("ipv6_ah header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
break; |
} |
|
hdr=(struct ipv6_opt_hdr *)skb->data+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* AH -> evaluate */ |
if (nexthdr == NEXTHDR_AUTH) { |
temp |= MASK_AH; |
break; |
} |
|
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_ah: new pointer too large! \n"); |
break; |
} |
} |
|
/* AH header not found */ |
if ( temp != MASK_AH ) return 0; |
|
if (len < (int)sizeof(struct ahhdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
ah = (struct ahhdr *) (skb->data + ptr); |
|
DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); |
DEBUGP("RES %04X ", ah->reserved); |
DEBUGP("SPI %u %08X\n", ntohl(ah->spi), ntohl(ah->spi)); |
|
DEBUGP("IPv6 AH spi %02X ", |
(spi_match(ahinfo->spis[0], ahinfo->spis[1], |
ntohl(ah->spi), |
!!(ahinfo->invflags & IP6T_AH_INV_SPI)))); |
DEBUGP("len %02X %04X %02X ", |
ahinfo->hdrlen, hdrlen, |
(!ahinfo->hdrlen || |
(ahinfo->hdrlen == hdrlen) ^ |
!!(ahinfo->invflags & IP6T_AH_INV_LEN))); |
DEBUGP("res %02X %04X %02X\n", |
ahinfo->hdrres, ah->reserved, |
!(ahinfo->hdrres && ah->reserved)); |
|
return (ah != NULL) |
&& |
(spi_match(ahinfo->spis[0], ahinfo->spis[1], |
ntohl(ah->spi), |
!!(ahinfo->invflags & IP6T_AH_INV_SPI))) |
&& |
(!ahinfo->hdrlen || |
(ahinfo->hdrlen == hdrlen) ^ |
!!(ahinfo->invflags & IP6T_AH_INV_LEN)) |
&& |
!(ahinfo->hdrres && ah->reserved); |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_ah *ahinfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_ah))) { |
DEBUGP("ip6t_ah: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_ah))); |
return 0; |
} |
if (ahinfo->invflags & ~IP6T_AH_INV_MASK) { |
DEBUGP("ip6t_ah: unknown flags %X\n", |
ahinfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match ah_match |
= { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&ah_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&ah_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/ip6t_owner.c
0,0 → 1,158
/* Kernel module to match various things tied to sockets associated with |
locally generated outgoing packets. |
|
Copyright (C) 2000,2001 Marc Boucher |
*/ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/file.h> |
#include <net/sock.h> |
|
#include <linux/netfilter_ipv6/ip6t_owner.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); |
MODULE_DESCRIPTION("IP6 tables owner matching module"); |
MODULE_LICENSE("GPL"); |
|
static int |
match_pid(const struct sk_buff *skb, pid_t pid) |
{ |
struct task_struct *p; |
struct files_struct *files; |
int i; |
|
read_lock(&tasklist_lock); |
p = find_task_by_pid(pid); |
if (!p) |
goto out; |
task_lock(p); |
files = p->files; |
if(files) { |
read_lock(&files->file_lock); |
for (i=0; i < files->max_fds; i++) { |
if (fcheck_files(files, i) == skb->sk->socket->file) { |
read_unlock(&files->file_lock); |
task_unlock(p); |
read_unlock(&tasklist_lock); |
return 1; |
} |
} |
read_unlock(&files->file_lock); |
} |
task_unlock(p); |
out: |
read_unlock(&tasklist_lock); |
return 0; |
} |
|
static int |
match_sid(const struct sk_buff *skb, pid_t sid) |
{ |
struct task_struct *p; |
struct file *file = skb->sk->socket->file; |
int i, found=0; |
|
read_lock(&tasklist_lock); |
for_each_task(p) { |
struct files_struct *files; |
if (p->session != sid) |
continue; |
|
task_lock(p); |
files = p->files; |
if (files) { |
read_lock(&files->file_lock); |
for (i=0; i < files->max_fds; i++) { |
if (fcheck_files(files, i) == file) { |
found = 1; |
break; |
} |
} |
read_unlock(&files->file_lock); |
} |
task_unlock(p); |
if(found) |
break; |
} |
read_unlock(&tasklist_lock); |
|
return found; |
} |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_owner_info *info = matchinfo; |
|
if (!skb->sk || !skb->sk->socket || !skb->sk->socket->file) |
return 0; |
|
if(info->match & IP6T_OWNER_UID) { |
if((skb->sk->socket->file->f_uid != info->uid) ^ |
!!(info->invert & IP6T_OWNER_UID)) |
return 0; |
} |
|
if(info->match & IP6T_OWNER_GID) { |
if((skb->sk->socket->file->f_gid != info->gid) ^ |
!!(info->invert & IP6T_OWNER_GID)) |
return 0; |
} |
|
if(info->match & IP6T_OWNER_PID) { |
if (!match_pid(skb, info->pid) ^ |
!!(info->invert & IP6T_OWNER_PID)) |
return 0; |
} |
|
if(info->match & IP6T_OWNER_SID) { |
if (!match_sid(skb, info->sid) ^ |
!!(info->invert & IP6T_OWNER_SID)) |
return 0; |
} |
|
return 1; |
} |
|
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (hook_mask |
& ~((1 << NF_IP6_LOCAL_OUT) | (1 << NF_IP6_POST_ROUTING))) { |
printk("ip6t_owner: only valid for LOCAL_OUT or POST_ROUTING.\n"); |
return 0; |
} |
|
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_owner_info))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match owner_match |
= { { NULL, NULL }, "owner", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&owner_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&owner_match); |
} |
|
module_init(init); |
module_exit(fini); |
/ip6t_dst.c
0,0 → 1,276
/* Kernel module to match Hop-by-Hop and Destination parameters. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ipv6.h> |
#include <linux/types.h> |
#include <net/checksum.h> |
#include <net/ipv6.h> |
|
#include <asm/byteorder.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_opts.h> |
|
#define LOW(n) (n & 0x00FF) |
|
#define HOPBYHOP 0 |
|
EXPORT_NO_SYMBOLS; |
MODULE_LICENSE("GPL"); |
#if HOPBYHOP |
MODULE_DESCRIPTION("IPv6 HbH match"); |
#else |
MODULE_DESCRIPTION("IPv6 DST match"); |
#endif |
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
|
#if 0 |
#define DEBUGP printk |
#else |
#define DEBUGP(format, args...) |
#endif |
|
/* |
* (Type & 0xC0) >> 6 |
* 0 -> ignorable |
* 1 -> must drop the packet |
* 2 -> send ICMP PARM PROB regardless and drop packet |
* 3 -> Send ICMP if not a multicast address and drop packet |
* (Type & 0x20) >> 5 |
* 0 -> invariant |
* 1 -> can change the routing |
* (Type & 0x1F) Type |
* 0 -> PAD0 (only 1 byte!) |
* 1 -> PAD1 LENGTH info (total length = length + 2) |
* C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k ) |
* 5 -> RTALERT 2 x x |
*/ |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *protohdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
struct ipv6_opt_hdr *optsh = NULL; |
const struct ip6t_opts *optinfo = matchinfo; |
unsigned int temp; |
unsigned int len; |
u8 nexthdr; |
unsigned int ptr; |
unsigned int hdrlen = 0; |
unsigned int ret = 0; |
u_int16_t *optdesc = NULL; |
|
/* type of the 1st exthdr */ |
nexthdr = skb->nh.ipv6h->nexthdr; |
/* pointer to the 1st exthdr */ |
ptr = sizeof(struct ipv6hdr); |
/* available length */ |
len = skb->len - ptr; |
temp = 0; |
|
while (ip6t_ext_hdr(nexthdr)) { |
struct ipv6_opt_hdr *hdr; |
|
DEBUGP("ipv6_opts header iteration \n"); |
|
/* Is there enough space for the next ext header? */ |
if (len < (int)sizeof(struct ipv6_opt_hdr)) |
return 0; |
/* No more exthdr -> evaluate */ |
if (nexthdr == NEXTHDR_NONE) { |
break; |
} |
/* ESP -> evaluate */ |
if (nexthdr == NEXTHDR_ESP) { |
break; |
} |
|
hdr=(void *)(skb->data)+ptr; |
|
/* Calculate the header length */ |
if (nexthdr == NEXTHDR_FRAGMENT) { |
hdrlen = 8; |
} else if (nexthdr == NEXTHDR_AUTH) |
hdrlen = (hdr->hdrlen+2)<<2; |
else |
hdrlen = ipv6_optlen(hdr); |
|
/* OPTS -> evaluate */ |
#if HOPBYHOP |
if (nexthdr == NEXTHDR_HOP) { |
temp |= MASK_HOPOPTS; |
#else |
if (nexthdr == NEXTHDR_DEST) { |
temp |= MASK_DSTOPTS; |
#endif |
break; |
} |
|
|
/* set the flag */ |
switch (nexthdr){ |
case NEXTHDR_HOP: |
case NEXTHDR_ROUTING: |
case NEXTHDR_FRAGMENT: |
case NEXTHDR_AUTH: |
case NEXTHDR_DEST: |
break; |
default: |
DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr); |
return 0; |
break; |
} |
|
nexthdr = hdr->nexthdr; |
len -= hdrlen; |
ptr += hdrlen; |
if ( ptr > skb->len ) { |
DEBUGP("ipv6_opts: new pointer is too large! \n"); |
break; |
} |
} |
|
/* OPTIONS header not found */ |
#if HOPBYHOP |
if ( temp != MASK_HOPOPTS ) return 0; |
#else |
if ( temp != MASK_DSTOPTS ) return 0; |
#endif |
|
if (len < (int)sizeof(struct ipv6_opt_hdr)){ |
*hotdrop = 1; |
return 0; |
} |
|
if (len < hdrlen){ |
/* Packet smaller than it's length field */ |
return 0; |
} |
|
optsh=(void *)(skb->data)+ptr; |
|
DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen); |
|
DEBUGP("len %02X %04X %02X ", |
optinfo->hdrlen, hdrlen, |
(!(optinfo->flags & IP6T_OPTS_LEN) || |
((optinfo->hdrlen == hdrlen) ^ |
!!(optinfo->invflags & IP6T_OPTS_INV_LEN)))); |
|
ret = (optsh != NULL) |
&& |
(!(optinfo->flags & IP6T_OPTS_LEN) || |
((optinfo->hdrlen == hdrlen) ^ |
!!(optinfo->invflags & IP6T_OPTS_INV_LEN))); |
|
temp = len = 0; |
ptr += 2; |
hdrlen -= 2; |
if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){ |
return ret; |
} else if (optinfo->flags & IP6T_OPTS_NSTRICT) { |
DEBUGP("Not strict - not implemented"); |
} else { |
DEBUGP("Strict "); |
DEBUGP("#%d ",optinfo->optsnr); |
for(temp=0; temp<optinfo->optsnr; temp++){ |
optdesc = (void *)(skb->data)+ptr; |
/* Type check */ |
if ( (unsigned char)*optdesc != |
(optinfo->opts[temp] & 0xFF00)>>8 ){ |
DEBUGP("Tbad %02X %02X\n", |
(unsigned char)*optdesc, |
(optinfo->opts[temp] & |
0xFF00)>>8); |
return 0; |
} else { |
DEBUGP("Tok "); |
} |
/* Length check */ |
if (((optinfo->opts[temp] & 0x00FF) != 0xFF) && |
(unsigned char)*optdesc != 0){ |
if ( ntohs((u16)*optdesc) != |
optinfo->opts[temp] ){ |
DEBUGP("Lbad %02X %04X %04X\n", |
(unsigned char)*optdesc, |
ntohs((u16)*optdesc), |
optinfo->opts[temp]); |
return 0; |
} else { |
DEBUGP("Lok "); |
} |
} |
/* Step to the next */ |
if ((unsigned char)*optdesc == 0){ |
DEBUGP("PAD0 \n"); |
ptr++; |
hdrlen--; |
} else { |
ptr += LOW(ntohs(*optdesc)); |
hdrlen -= LOW(ntohs(*optdesc)); |
DEBUGP("len%04X \n", |
LOW(ntohs(*optdesc))); |
} |
if (ptr > skb->len || ( !hdrlen && |
(temp != optinfo->optsnr - 1))) { |
DEBUGP("new pointer is too large! \n"); |
break; |
} |
} |
if (temp == optinfo->optsnr) |
return ret; |
else return 0; |
} |
|
return 0; |
} |
|
/* Called when user tries to insert an entry of this type. */ |
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchinfosize, |
unsigned int hook_mask) |
{ |
const struct ip6t_opts *optsinfo = matchinfo; |
|
if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) { |
DEBUGP("ip6t_opts: matchsize %u != %u\n", |
matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts))); |
return 0; |
} |
if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) { |
DEBUGP("ip6t_opts: unknown flags %X\n", |
optsinfo->invflags); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_match opts_match |
#if HOPBYHOP |
= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE }; |
#else |
= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE }; |
#endif |
|
static int __init init(void) |
{ |
return ip6t_register_match(&opts_match); |
} |
|
static void __exit cleanup(void) |
{ |
ip6t_unregister_match(&opts_match); |
} |
|
module_init(init); |
module_exit(cleanup); |
/ip6t_mark.c
0,0 → 1,51
/* Kernel module to match NFMARK values. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
|
#include <linux/netfilter_ipv6/ip6t_mark.h> |
#include <linux/netfilter_ipv6/ip6_tables.h> |
|
static int |
match(const struct sk_buff *skb, |
const struct net_device *in, |
const struct net_device *out, |
const void *matchinfo, |
int offset, |
const void *hdr, |
u_int16_t datalen, |
int *hotdrop) |
{ |
const struct ip6t_mark_info *info = matchinfo; |
|
return ((skb->nfmark & info->mask) == info->mark) ^ info->invert; |
} |
|
static int |
checkentry(const char *tablename, |
const struct ip6t_ip6 *ip, |
void *matchinfo, |
unsigned int matchsize, |
unsigned int hook_mask) |
{ |
if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_mark_info))) |
return 0; |
|
return 1; |
} |
|
static struct ip6t_match mark_match |
= { { NULL, NULL }, "mark", &match, &checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
return ip6t_register_match(&mark_match); |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_match(&mark_match); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |
/Makefile
0,0 → 1,35
# |
# Makefile for the netfilter modules on top of IPv6. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definition is now in the main makefile... |
|
O_TARGET := netfilter.o |
|
export-objs := ip6_tables.o |
|
# Link order matters here. |
obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o |
obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o |
obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o |
obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o |
obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o |
obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o |
obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o |
obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o |
obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o |
obj-$(CONFIG_IP6_NF_MATCH_AHESP) += ip6t_esp.o ip6t_ah.o |
obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o |
obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o |
obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o |
obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o |
obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o |
obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o |
obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o |
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o |
obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o |
|
include $(TOPDIR)/Rules.make |
/ip6t_MARK.c
0,0 → 1,68
/* This is a module which is used for setting the NFMARK field of an skb. */ |
#include <linux/module.h> |
#include <linux/skbuff.h> |
#include <linux/ip.h> |
#include <net/checksum.h> |
|
#include <linux/netfilter_ipv6/ip6_tables.h> |
#include <linux/netfilter_ipv6/ip6t_MARK.h> |
|
static unsigned int |
target(struct sk_buff **pskb, |
unsigned int hooknum, |
const struct net_device *in, |
const struct net_device *out, |
const void *targinfo, |
void *userinfo) |
{ |
const struct ip6t_mark_target_info *markinfo = targinfo; |
|
if((*pskb)->nfmark != markinfo->mark) { |
(*pskb)->nfmark = markinfo->mark; |
(*pskb)->nfcache |= NFC_ALTERED; |
} |
return IP6T_CONTINUE; |
} |
|
static int |
checkentry(const char *tablename, |
const struct ip6t_entry *e, |
void *targinfo, |
unsigned int targinfosize, |
unsigned int hook_mask) |
{ |
if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_mark_target_info))) { |
printk(KERN_WARNING "MARK: targinfosize %u != %Zu\n", |
targinfosize, |
IP6T_ALIGN(sizeof(struct ip6t_mark_target_info))); |
return 0; |
} |
|
if (strcmp(tablename, "mangle") != 0) { |
printk(KERN_WARNING "MARK: can only be called from \"mangle\" table, not \"%s\"\n", tablename); |
return 0; |
} |
|
return 1; |
} |
|
static struct ip6t_target ip6t_mark_reg |
= { { NULL, NULL }, "MARK", target, checkentry, NULL, THIS_MODULE }; |
|
static int __init init(void) |
{ |
printk(KERN_DEBUG "registering ipv6 mark target\n"); |
if (ip6t_register_target(&ip6t_mark_reg)) |
return -EINVAL; |
|
return 0; |
} |
|
static void __exit fini(void) |
{ |
ip6t_unregister_target(&ip6t_mark_reg); |
} |
|
module_init(init); |
module_exit(fini); |
MODULE_LICENSE("GPL"); |