diff options
| author | David S. Miller <davem@kernel.bkbits.net> | 2003-03-22 12:12:14 -0800 |
|---|---|---|
| committer | David S. Miller <davem@kernel.bkbits.net> | 2003-03-22 12:12:14 -0800 |
| commit | 584d39284634f632fa3d4e72bb751dde37201e2f (patch) | |
| tree | e49955ac42d300ded9273ddf59bec3e781e1a6ad | |
| parent | db51569ef340e1b87df200aca836216ee757fb17 (diff) | |
| parent | 4228d368e67f05d8cfc52f80f0a2b94d91320d1c (diff) | |
Merge davem@nuts.ninka.net:/home/davem/src/BK/net-2.5
into kernel.bkbits.net:/home/davem/net-2.5
50 files changed, 2256 insertions, 1928 deletions
diff --git a/crypto/aes.c b/crypto/aes.c index 5f68d8ae3ce2..d46ac3bd0692 100644 --- a/crypto/aes.c +++ b/crypto/aes.c @@ -55,6 +55,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/errno.h> #include <linux/crypto.h> #include <asm/byteorder.h> diff --git a/crypto/api.c b/crypto/api.c index 53c7ff8393a2..df1dde05e540 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -15,6 +15,7 @@ */ #include <linux/init.h> #include <linux/crypto.h> +#include <linux/errno.h> #include <linux/rwsem.h> #include <linux/slab.h> #include "internal.h" diff --git a/crypto/des.c b/crypto/des.c index edc648ed891b..cd3dcb548b61 100644 --- a/crypto/des.c +++ b/crypto/des.c @@ -23,6 +23,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/errno.h> #include <asm/scatterlist.h> #include <linux/crypto.h> diff --git a/crypto/digest.c b/crypto/digest.c index 400f23566f69..e82470a472be 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -13,6 +13,7 @@ */ #include <linux/crypto.h> #include <linux/mm.h> +#include <linux/errno.h> #include <linux/highmem.h> #include <asm/scatterlist.h> #include "internal.h" diff --git a/crypto/serpent.c b/crypto/serpent.c index 35587654cc4a..080ae193aef6 100644 --- a/crypto/serpent.c +++ b/crypto/serpent.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/errno.h> #include <asm/byteorder.h> #include <linux/crypto.h> diff --git a/crypto/twofish.c b/crypto/twofish.c index 0744f9590bfd..af2de8d63299 100644 --- a/crypto/twofish.c +++ b/crypto/twofish.c @@ -40,6 +40,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/errno.h> #include <linux/crypto.h> diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c index 8239161d003c..bba31ebc3ffc 100644 --- a/drivers/atm/idt77105.c +++ b/drivers/atm/idt77105.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/capability.h> #include <linux/atm_idt77105.h> +#include <linux/spinlock.h> #include <asm/system.h> #include <asm/param.h> #include <asm/uaccess.h> @@ -38,6 +39,7 @@ struct idt77105_priv { unsigned char old_mcr; /* storage of MCR reg while signal lost */ }; +static spinlock_t idt77105_priv_lock = SPIN_LOCK_UNLOCKED; #define PRIV(dev) ((struct idt77105_priv *) dev->phy_data) @@ -144,12 +146,11 @@ static int fetch_stats(struct atm_dev *dev,struct idt77105_stats *arg,int zero) unsigned long flags; struct idt77105_stats stats; - save_flags(flags); - cli(); + spin_lock_irqsave(&idt77105_priv_lock, flags); memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats)); if (zero) memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats)); - restore_flags(flags); + spin_unlock_irqrestore(&idt77105_priv_lock, flags); if (arg == NULL) return 0; return copy_to_user(arg, &PRIV(dev)->stats, @@ -267,11 +268,10 @@ static int idt77105_start(struct atm_dev *dev) if (!(PRIV(dev) = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL))) return -ENOMEM; PRIV(dev)->dev = dev; - save_flags(flags); - cli(); + spin_lock_irqsave(&idt77105_priv_lock, flags); PRIV(dev)->next = idt77105_all; idt77105_all = PRIV(dev); - restore_flags(flags); + spin_unlock_irqrestore(&idt77105_priv_lock, flags); memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats)); /* initialise dev->signal from Good Signal Bit */ @@ -305,11 +305,9 @@ static int idt77105_start(struct atm_dev *dev) idt77105_stats_timer_func(0); /* clear 77105 counters */ (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ - cli(); - if (!start_timer) restore_flags(flags); - else { + spin_lock_irqsave(&idt77105_priv_lock, flags); + if (start_timer) { start_timer = 0; - restore_flags(flags); init_timer(&stats_timer); stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD; @@ -321,32 +319,11 @@ static int idt77105_start(struct atm_dev *dev) restart_timer.function = idt77105_restart_timer_func; add_timer(&restart_timer); } + spin_unlock_irqrestore(&idt77105_priv_lock, flags); return 0; } -static const struct atmphy_ops idt77105_ops = { - idt77105_start, - idt77105_ioctl, - idt77105_int -}; - - -int __init idt77105_init(struct atm_dev *dev) -{ - MOD_INC_USE_COUNT; - - dev->phy = &idt77105_ops; - return 0; -} - - -/* - * TODO: this function should be called through phy_ops - * but that will not be possible for some time as there is - * currently a freeze on modifying that structure - * -- Greg Banks, 13 Sep 1999 - */ int idt77105_stop(struct atm_dev *dev) { struct idt77105_priv *walk, *prev; @@ -372,30 +349,33 @@ int idt77105_stop(struct atm_dev *dev) } } - MOD_DEC_USE_COUNT; return 0; } +static const struct atmphy_ops idt77105_ops = { + .start = idt77105_start, + .ioctl = idt77105_ioctl, + .interrupt = idt77105_int, + .stop = idt77105_stop, +}; -EXPORT_SYMBOL(idt77105_init); -EXPORT_SYMBOL(idt77105_stop); - -MODULE_LICENSE("GPL"); - -#ifdef MODULE -int init_module(void) +int idt77105_init(struct atm_dev *dev) { + dev->phy = &idt77105_ops; return 0; } +EXPORT_SYMBOL(idt77105_init); -void cleanup_module(void) +static void __exit idt77105_exit(void) { /* turn off timers */ del_timer(&stats_timer); del_timer(&restart_timer); } -#endif +module_exit(idt77105_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index 3637abbf8a56..bfc3984cd868 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -354,11 +354,8 @@ static void __exit nicstar_module_exit(void) card = cards[i]; -#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105 - if (card->max_pcr == ATM_25_PCR) { - idt77105_stop(card->atmdev); - } -#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */ + if (card->atmdev->phy && card->atmdev->phy->stop) + card->atmdev->phy->stop(card->atmdev); /* Stop everything */ writel(0x00000000, card->membase + CFG); @@ -490,11 +487,6 @@ static int __init ns_init_card(int i, struct pci_dev *pcidev) card->atmdev = NULL; card->pcidev = pcidev; card->membase = pci_resource_start(pcidev, 1); -#ifdef __powerpc__ - /* Compensate for different memory map between host CPU and PCI bus. - Shouldn't we use a macro for this? */ - card->membase += KERNELBASE; -#endif /* __powerpc__ */ card->membase = (unsigned long) ioremap(card->membase, NS_IOREMAP_SIZE); if (card->membase == 0) { @@ -905,22 +897,13 @@ static int __init ns_init_card(int i, struct pci_dev *pcidev) card->atmdev->phy = NULL; #ifdef CONFIG_ATM_NICSTAR_USE_SUNI - if (card->max_pcr == ATM_OC3_PCR) { + if (card->max_pcr == ATM_OC3_PCR) suni_init(card->atmdev); - - MOD_INC_USE_COUNT; - /* Can't remove the nicstar driver or the suni driver would oops */ - } #endif /* CONFIG_ATM_NICSTAR_USE_SUNI */ #ifdef CONFIG_ATM_NICSTAR_USE_IDT77105 - if (card->max_pcr == ATM_25_PCR) { + if (card->max_pcr == ATM_25_PCR) idt77105_init(card->atmdev); - /* Note that for the IDT77105 PHY we don't need the awful - * module count hack that the SUNI needs because we can - * stop the '105 when the nicstar module is cleaned up. - */ - } #endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */ if (card->atmdev->phy && card->atmdev->phy->start) @@ -2327,6 +2310,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) { push_rxbufs(card, BUF_SM, (u32) skb, (u32) virt_to_bus(skb->data), 0, 0); + atomic_inc(&vcc->stats->rx_drop); } else { @@ -2354,6 +2338,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) { push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0); + atomic_inc(&vcc->stats->rx_drop); } else { @@ -2378,6 +2363,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) { push_rxbufs(card, BUF_LG, (u32) skb, (u32) virt_to_bus(skb->data), 0, 0); + atomic_inc(&vcc->stats->rx_drop); } else { @@ -2462,6 +2448,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) } else dev_kfree_skb_any(hb); + atomic_inc(&vcc->stats->rx_drop); } else { diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c index a30650ee2482..548f4ead189f 100644 --- a/drivers/atm/suni.c +++ b/drivers/atm/suni.c @@ -297,7 +297,7 @@ int suni_init(struct atm_dev *dev) mri = GET(MRI); /* reset SUNI */ PUT(mri | SUNI_MRI_RESET,MRI); PUT(mri,MRI); - PUT(0,MT); /* disable all tests */ + PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET, TPOP_APM); /* use SONET */ REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, @@ -307,24 +307,6 @@ int suni_init(struct atm_dev *dev) return 0; } - EXPORT_SYMBOL(suni_init); - MODULE_LICENSE("GPL"); - -#ifdef MODULE - - -int init_module(void) -{ - return 0; -} - - -void cleanup_module(void) -{ - /* Nay */ -} - -#endif diff --git a/drivers/atm/suni.h b/drivers/atm/suni.h index ae6d39abbddf..d14c835abc97 100644 --- a/drivers/atm/suni.h +++ b/drivers/atm/suni.h @@ -198,6 +198,7 @@ #define SUNI_MT_IOTST 0x04 /* RW, enable test mode */ #define SUNI_MT_DBCTRL 0x08 /* W, control data bus by CSB pin */ #define SUNI_MT_PMCTST 0x10 /* W, PMC test mode */ +#define SUNI_MT_DS27_53 0x80 /* RW, select between 8- or 16- bit */ #define SUNI_IDLE_PATTERN 0x6a /* idle pattern */ diff --git a/include/linux/in6.h b/include/linux/in6.h index 051db67aee69..8689112d08f3 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -180,5 +180,8 @@ struct in6_flowlabel_req #define IPV6_FLOWLABEL_MGR 32 #define IPV6_FLOWINFO_SEND 33 +#define IPV6_IPSEC_POLICY 34 +#define IPV6_XFRM_POLICY 35 + #endif diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index cb6348d30cb2..1056e450ef14 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -260,6 +260,7 @@ struct ebt_table unsigned int valid_hooks); /* the data used by the kernel */ struct ebt_table_info *private; + struct module *me; }; extern int ebt_register_table(struct ebt_table *table); diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index c6e4e6e29f10..efb41c857ea3 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -262,6 +262,14 @@ struct sadb_x_ipsecrequest { #define SADB_X_EALG_AESCBC 12 #define SADB_EALG_MAX 12 +/* Compression algorithms */ +#define SADB_X_CALG_NONE 0 +#define SADB_X_CALG_OUI 1 +#define SADB_X_CALG_DEFLATE 2 +#define SADB_X_CALG_LZS 3 +#define SADB_X_CALG_LZJH 4 +#define SADB_X_CALG_MAX 4 + /* Extension Header values */ #define SADB_EXT_RESERVED 0 #define SADB_EXT_SA 1 diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 7458d1f49472..9c39534d0267 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -12,12 +12,7 @@ */ typedef union { - struct { - __u32 addr; - __u32 mask; /* Use unused bits to cache mask. */ - } a4; -#define xfrm4_addr a4.addr -#define xfrm4_mask a4.mask + __u32 a4; __u32 a6[4]; } xfrm_address_t; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index fd99637a96bc..572d9ee31f0a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -10,8 +10,10 @@ #include <linux/pfkeyv2.h> #include <linux/in6.h> +#include <net/sock.h> #include <net/dst.h> #include <net/route.h> +#include <net/ipv6.h> #include <net/ip6_fib.h> #define XFRM_ALIGN8(len) (((len) + 7) & ~7) @@ -144,6 +146,51 @@ enum { XFRM_STATE_DEAD }; +struct xfrm_type; +struct xfrm_dst; +struct xfrm_policy_afinfo { + unsigned short family; + rwlock_t lock; + struct xfrm_type_map *type_map; + struct dst_ops *dst_ops; + void (*garbage_collect)(void); + int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl); + struct dst_entry *(*find_bundle)(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy); + int (*bundle_create)(struct xfrm_policy *policy, + struct xfrm_state **xfrm, + int nx, + struct flowi *fl, + struct dst_entry **dst_p); + void (*decode_session)(struct sk_buff *skb, + struct flowi *fl); +}; + +extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); +extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo); +extern struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); +extern void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); + +#define XFRM_ACQ_EXPIRES 30 + +struct xfrm_tmpl; +struct xfrm_state_afinfo { + unsigned short family; + rwlock_t lock; + struct list_head *state_bydst; + struct list_head *state_byspi; + void (*init_tempsel)(struct xfrm_state *x, struct flowi *fl, + struct xfrm_tmpl *tmpl, + xfrm_address_t *daddr, xfrm_address_t *saddr); + struct xfrm_state *(*state_lookup)(xfrm_address_t *daddr, u32 spi, u8 proto); + struct xfrm_state *(*find_acq)(u8 mode, u16 reqid, u8 proto, + xfrm_address_t *daddr, xfrm_address_t *saddr, + int create); +}; + +extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); +extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); +extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family); +extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); struct xfrm_type { @@ -159,9 +206,14 @@ struct xfrm_type u32 (*get_max_size)(struct xfrm_state *, int size); }; -extern int xfrm_register_type(struct xfrm_type *type); -extern int xfrm_unregister_type(struct xfrm_type *type); -extern struct xfrm_type *xfrm_get_type(u8 proto); +struct xfrm_type_map { + rwlock_t lock; + struct xfrm_type *map[256]; +}; + +extern int xfrm_register_type(struct xfrm_type *type, unsigned short family); +extern int xfrm_unregister_type(struct xfrm_type *type, unsigned short family); +extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family); extern void xfrm_put_type(struct xfrm_type *type); struct xfrm_tmpl @@ -232,6 +284,47 @@ extern int xfrm_register_km(struct xfrm_mgr *km); extern int xfrm_unregister_km(struct xfrm_mgr *km); +#define XFRM_FLOWCACHE_HASH_SIZE 1024 + +static inline u32 __flow_hash4(struct flowi *fl) +{ + u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport; + + hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4); + + hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport; + hash ^= (hash >> 10); + hash ^= (hash >> 20); + return hash & (XFRM_FLOWCACHE_HASH_SIZE-1); +} + +static inline u32 __flow_hash6(struct flowi *fl) +{ + u32 hash = fl->fl6_src->s6_addr32[2] ^ + fl->fl6_src->s6_addr32[3] ^ + fl->uli_u.ports.sport; + + hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4); + + hash ^= fl->fl6_dst->s6_addr32[2] ^ + fl->fl6_dst->s6_addr32[3] ^ + fl->uli_u.ports.dport; + hash ^= (hash >> 10); + hash ^= (hash >> 20); + return hash & (XFRM_FLOWCACHE_HASH_SIZE-1); +} + +static inline u32 flow_hash(struct flowi *fl, unsigned short family) +{ + switch (family) { + case AF_INET: + return __flow_hash4(fl); + case AF_INET6: + return __flow_hash6(fl); + } + return 0; /*XXX*/ +} + extern struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2]; static inline void xfrm_pol_hold(struct xfrm_policy *policy) @@ -248,6 +341,68 @@ static inline void xfrm_pol_put(struct xfrm_policy *policy) __xfrm_policy_destroy(policy); } +#define XFRM_DST_HSIZE 1024 + +static __inline__ +unsigned __xfrm4_dst_hash(xfrm_address_t *addr) +{ + unsigned h; + h = ntohl(addr->a4); + h = (h ^ (h>>16)) % XFRM_DST_HSIZE; + return h; +} + +static __inline__ +unsigned __xfrm6_dst_hash(xfrm_address_t *addr) +{ + unsigned h; + h = ntohl(addr->a6[2]^addr->a6[3]); + h = (h ^ (h>>16)) % XFRM_DST_HSIZE; + return h; +} + +static __inline__ +unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family) +{ + switch (family) { + case AF_INET: + return __xfrm4_dst_hash(addr); + case AF_INET6: + return __xfrm6_dst_hash(addr); + } + return 0; +} + +static __inline__ +unsigned __xfrm4_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto) +{ + unsigned h; + h = ntohl(addr->a4^spi^proto); + h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + return h; +} + +static __inline__ +unsigned __xfrm6_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto) +{ + unsigned h; + h = ntohl(addr->a6[2]^addr->a6[3]^spi^proto); + h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + return h; +} + +static __inline__ +unsigned xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family) +{ + switch (family) { + case AF_INET: + return __xfrm4_spi_hash(addr, spi, proto); + case AF_INET6: + return __xfrm6_spi_hash(addr, spi, proto); + } + return 0; /*XXX*/ +} + extern void __xfrm_state_destroy(struct xfrm_state *); static inline void xfrm_state_put(struct xfrm_state *x) @@ -261,15 +416,65 @@ static inline void xfrm_state_hold(struct xfrm_state *x) atomic_inc(&x->refcnt); } +static __inline__ int addr_match(void *token1, void *token2, int prefixlen) +{ + __u32 *a1 = token1; + __u32 *a2 = token2; + int pdw; + int pbi; + + pdw = prefixlen >> 5; /* num of whole __u32 in prefix */ + pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ + + if (pdw) + if (memcmp(a1, a2, pdw << 2)) + return 0; + + if (pbi) { + __u32 mask; + + mask = htonl((0xffffffff) << (32 - pbi)); + + if ((a1[pdw] ^ a2[pdw]) & mask) + return 0; + } + + return 1; +} + +static inline int +__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) +{ + return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) && + addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) && + !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) && + !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) && + (fl->proto == sel->proto || !sel->proto) && + (fl->oif == sel->ifindex || !sel->ifindex); +} + static inline int -xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) +__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl) { - return !((fl->fl4_dst^sel->daddr.xfrm4_addr)&sel->daddr.xfrm4_mask) && + return addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) && + addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) && !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) && !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) && (fl->proto == sel->proto || !sel->proto) && - (fl->oif == sel->ifindex || !sel->ifindex) && - !((fl->fl4_src^sel->saddr.xfrm4_addr)&sel->saddr.xfrm4_mask); + (fl->oif == sel->ifindex || !sel->ifindex); +} + +static inline int +xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, + unsigned short family) +{ + switch (family) { + case AF_INET: + return __xfrm4_selector_match(sel, fl); + case AF_INET6: + return __xfrm6_selector_match(sel, fl); + } + return 0; } /* A struct encoding bundle of transformations to apply to some set of flow. @@ -295,6 +500,7 @@ struct xfrm_dst struct sec_path { + kmem_cache_t *pool; atomic_t refcnt; int len; struct xfrm_state *xvec[XFRM_MAX_DEPTH]; @@ -316,42 +522,73 @@ secpath_put(struct sec_path *sp) if (sp && atomic_dec_and_test(&sp->refcnt)) __secpath_destroy(sp); } + +static inline int +__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x) +{ + return (tmpl->saddr.a4 && + tmpl->saddr.a4 != x->props.saddr.a4); +} + +static inline int +__xfrm6_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x) +{ + return (!ipv6_addr_any((struct in6_addr*)&tmpl->saddr) && + ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr)); +} + +static inline int +xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short family) +{ + switch (family) { + case AF_INET: + return __xfrm4_state_addr_cmp(tmpl, x); + case AF_INET6: + return __xfrm6_state_addr_cmp(tmpl, x); + } + return !0; +} + extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family); -static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb) +static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family) { if (sk && sk->policy[XFRM_POLICY_IN]) - return __xfrm_policy_check(sk, dir, skb, AF_INET); + return __xfrm_policy_check(sk, dir, skb, family); return !xfrm_policy_list[dir] || (skb->dst->flags & DST_NOPOLICY) || - __xfrm_policy_check(sk, dir, skb, AF_INET); + __xfrm_policy_check(sk, dir, skb, family); +} + +static inline int xfrm4_policy_check(struct sock *sk, int dir, struct sk_buff *skb) +{ + return xfrm_policy_check(sk, dir, skb, AF_INET); } static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb) { - if (sk && sk->policy[XFRM_POLICY_IN]) - return __xfrm_policy_check(sk, dir, skb, AF_INET6); - - return !xfrm_policy_list[dir] || - (skb->dst->flags & DST_NOPOLICY) || - __xfrm_policy_check(sk, dir, skb, AF_INET6); + return xfrm_policy_check(sk, dir, skb, AF_INET6); } + extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family); -static inline int xfrm_route_forward(struct sk_buff *skb) +static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { return !xfrm_policy_list[XFRM_POLICY_OUT] || (skb->dst->flags & DST_NOXFRM) || - __xfrm_route_forward(skb, AF_INET); + __xfrm_route_forward(skb, family); +} + +static inline int xfrm4_route_forward(struct sk_buff *skb) +{ + return xfrm_route_forward(skb, AF_INET); } static inline int xfrm6_route_forward(struct sk_buff *skb) { - return !xfrm_policy_list[XFRM_POLICY_OUT] || - (skb->dst->flags & DST_NOXFRM) || - __xfrm_route_forward(skb, AF_INET6); + return xfrm_route_forward(skb, AF_INET6); } extern int __xfrm_sk_clone_policy(struct sock *sk); @@ -377,6 +614,66 @@ static inline void xfrm_sk_free_policy(struct sock *sk) } } +static __inline__ +xfrm_address_t *xfrm_flowi_daddr(struct flowi *fl, unsigned short family) +{ + switch (family){ + case AF_INET: + return (xfrm_address_t *)&fl->fl4_dst; + case AF_INET6: + return (xfrm_address_t *)fl->fl6_dst; + } + return NULL; +} + +static __inline__ +xfrm_address_t *xfrm_flowi_saddr(struct flowi *fl, unsigned short family) +{ + switch (family){ + case AF_INET: + return (xfrm_address_t *)&fl->fl4_src; + case AF_INET6: + return (xfrm_address_t *)fl->fl6_src; + } + return NULL; +} + +static __inline__ int +__xfrm4_state_addr_check(struct xfrm_state *x, + xfrm_address_t *daddr, xfrm_address_t *saddr) +{ + if (daddr->a4 == x->id.daddr.a4 && + (saddr->a4 == x->props.saddr.a4 || !saddr->a4 || !x->props.saddr.a4)) + return 1; + return 0; +} + +static __inline__ int +__xfrm6_state_addr_check(struct xfrm_state *x, + xfrm_address_t *daddr, xfrm_address_t *saddr) +{ + if (!ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) && + (!ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr)|| + ipv6_addr_any((struct in6_addr *)saddr) || + ipv6_addr_any((struct in6_addr *)&x->props.saddr))) + return 1; + return 0; +} + +static __inline__ int +xfrm_state_addr_check(struct xfrm_state *x, + xfrm_address_t *daddr, xfrm_address_t *saddr, + unsigned short family) +{ + switch (family) { + case AF_INET: + return __xfrm4_state_addr_check(x, daddr, saddr); + case AF_INET6: + return __xfrm6_state_addr_check(x, daddr, saddr); + } + return 0; +} + /* * xfrm algorithm information */ @@ -390,30 +687,42 @@ struct xfrm_algo_encr_info { u16 defkeybits; }; +struct xfrm_algo_comp_info { + u16 threshold; +}; + struct xfrm_algo_desc { char *name; u8 available:1; union { struct xfrm_algo_auth_info auth; struct xfrm_algo_encr_info encr; + struct xfrm_algo_comp_info comp; } uinfo; struct sadb_alg desc; }; +extern void xfrm_init(void); +extern void xfrm4_init(void); +extern void xfrm4_fini(void); +extern void xfrm6_init(void); +extern void xfrm6_fini(void); extern void xfrm_state_init(void); -extern void xfrm_input_init(void); +extern void xfrm4_state_init(void); +extern void xfrm4_state_fini(void); +extern void xfrm6_state_init(void); +extern void xfrm6_state_fini(void); + extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *); extern struct xfrm_state *xfrm_state_alloc(void); -extern struct xfrm_state *xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, - struct xfrm_policy *pol, int *err); -extern struct xfrm_state *xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, - struct flowi *fl, struct xfrm_tmpl *tmpl, - struct xfrm_policy *pol, int *err); +extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, + struct flowi *fl, struct xfrm_tmpl *tmpl, + struct xfrm_policy *pol, int *err, + unsigned short family); extern int xfrm_state_check_expire(struct xfrm_state *x); extern void xfrm_state_insert(struct xfrm_state *x); extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb); -extern struct xfrm_state *xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto); -extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto); +extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family); extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq); extern void xfrm_state_delete(struct xfrm_state *x); extern void xfrm_state_flush(u8 proto); @@ -425,6 +734,9 @@ extern int xfrm6_rcv(struct sk_buff *skb); extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir); extern int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen); +void xfrm_policy_init(void); +void xfrm4_policy_init(void); +void xfrm6_policy_init(void); struct xfrm_policy *xfrm_policy_alloc(int gfp); extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *); struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned short family); @@ -433,76 +745,39 @@ struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel); struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete); void xfrm_policy_flush(void); void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); -struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create); -struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, - struct in6_addr *saddr, int create); +struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, + xfrm_address_t *daddr, xfrm_address_t *saddr, + int create, unsigned short family); extern void xfrm_policy_flush(void); extern void xfrm_policy_kill(struct xfrm_policy *); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl); extern int xfrm_flush_bundles(struct xfrm_state *x); +extern int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family); extern wait_queue_head_t km_waitq; extern void km_warn_expired(struct xfrm_state *x); extern void km_expired(struct xfrm_state *x); extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol); +extern void xfrm4_input_init(void); +extern void xfrm6_input_init(void); +extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq); + extern void xfrm_probe_algs(void); extern int xfrm_count_auth_supported(void); extern int xfrm_count_enc_supported(void); extern struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx); extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx); +extern struct xfrm_algo_desc *xfrm_calg_get_byidx(unsigned int idx); extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id); extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id); +extern struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id); extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name); extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name); - -static __inline__ int addr_match(void *token1, void *token2, int prefixlen) -{ - __u32 *a1 = token1; - __u32 *a2 = token2; - int pdw; - int pbi; - - pdw = prefixlen >> 5; /* num of whole __u32 in prefix */ - pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ - - if (pdw) - if (memcmp(a1, a2, pdw << 2)) - return 0; - - if (pbi) { - __u32 mask; - - mask = htonl((0xffffffff) << (32 - pbi)); - - if ((a1[pdw] ^ a2[pdw]) & mask) - return 0; - } - - return 1; -} - -static inline int -xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl) -{ - return addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) && - addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) && - !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) && - !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) && - (fl->proto == sel->proto || !sel->proto) && - (fl->oif == sel->ifindex || !sel->ifindex); -} - -extern int xfrm6_register_type(struct xfrm_type *type); -extern int xfrm6_unregister_type(struct xfrm_type *type); -extern struct xfrm_type *xfrm6_get_type(u8 proto); +extern struct xfrm_algo_desc *xfrm_calg_get_byname(char *name); struct crypto_tfm; typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsigned int); -typedef int (xfrm_dst_lookup_t)(struct xfrm_dst **dst, struct flowi *fl); -int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup, unsigned short family); -void xfrm_dst_lookup_unregister(unsigned short family); - #endif /* _NET_XFRM_H */ diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 1b21c8f6ac73..948eb4bb6fc0 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -49,6 +49,7 @@ static struct ebt_table broute_table = .valid_hooks = 1 << NF_BR_BROUTING, .lock = RW_LOCK_UNLOCKED, .check = check, + .me = THIS_MODULE, }; static int ebt_broute(struct sk_buff **pskb) diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index d4ceea50978e..ce60c4ea527d 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -57,6 +57,7 @@ static struct ebt_table frame_filter = .valid_hooks = FILTER_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .check = check, + .me = THIS_MODULE, }; static unsigned int diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 694397f4d1e6..8dcf54528612 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -56,6 +56,7 @@ static struct ebt_table frame_nat = .valid_hooks = NAT_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .check = check, + .me = THIS_MODULE, }; static unsigned int diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index e29790052278..b53e98f47e3d 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -969,8 +969,10 @@ static int do_replace(void *user, unsigned int len) goto free_counterstmp; t = find_table_lock(tmp.name, &ret, &ebt_mutex); - if (!t) + if (!t) { + ret = -ENOENT; goto free_iterate; + } /* the table doesn't like it */ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks))) @@ -984,6 +986,12 @@ static int do_replace(void *user, unsigned int len) /* we have the mutex lock, so no danger in reading this pointer */ table = t->private; + /* make sure the table can only be rmmod'ed if it contains no rules */ + if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) { + ret = -ENOENT; + goto free_unlock; + } else if (table->nentries && !newinfo->nentries) + module_put(t->me); /* we need an atomic snapshot of the counters */ write_lock_bh(&t->lock); if (tmp.num_counters) @@ -1168,6 +1176,11 @@ int ebt_register_table(struct ebt_table *table) goto free_unlock; } + /* Hold a reference count if the chains aren't empty */ + if (newinfo->nentries && !try_module_get(table->me)) { + ret = -ENOENT; + goto free_unlock; + } list_prepend(&ebt_tables, table); up(&ebt_mutex); return 0; @@ -1196,8 +1209,6 @@ void ebt_unregister_table(struct ebt_table *table) down(&ebt_mutex); LIST_DELETE(&ebt_tables, table); up(&ebt_mutex); - EBT_ENTRY_ITERATE(table->private->entries, - table->private->entries_size, ebt_cleanup_entry, NULL); if (table->private->entries) vfree(table->private->entries); if (table->private->chainstack) { diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7ce979fcaae4..c4a400a34f73 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_XFRM_USER) += xfrm_user.o -obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o +obj-y += xfrm_policy.o xfrm4_policy.o xfrm_state.o xfrm4_state.o xfrm_input.o xfrm4_input.o xfrm_algo.o diff --git a/net/ipv4/ah.c b/net/ipv4/ah.c index efb9d1d8114a..e7404e9ff2d7 100644 --- a/net/ipv4/ah.c +++ b/net/ipv4/ah.c @@ -92,8 +92,8 @@ static int ah_output(struct sk_buff *skb) top_iph->ttl = 0; top_iph->protocol = IPPROTO_AH; top_iph->check = 0; - top_iph->saddr = x->props.saddr.xfrm4_addr; - top_iph->daddr = x->id.daddr.xfrm4_addr; + top_iph->saddr = x->props.saddr.a4; + top_iph->daddr = x->id.daddr.a4; ah = (struct ip_auth_hdr*)(top_iph+1); ah->nexthdr = IPPROTO_IPIP; } else { @@ -232,7 +232,7 @@ void ah4_err(struct sk_buff *skb, u32 info) skb->h.icmph->code != ICMP_FRAG_NEEDED) return; - x = xfrm4_state_lookup(iph->daddr, ah->spi, IPPROTO_AH); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET); if (!x) return; printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n", @@ -338,13 +338,13 @@ static struct inet_protocol ah4_protocol = { static int __init ah4_init(void) { SET_MODULE_OWNER(&ah_type); - if (xfrm_register_type(&ah_type) < 0) { + if (xfrm_register_type(&ah_type, AF_INET) < 0) { printk(KERN_INFO "ip ah init: can't add xfrm type\n"); return -EAGAIN; } if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) { printk(KERN_INFO "ip ah init: can't add protocol\n"); - xfrm_unregister_type(&ah_type); + xfrm_unregister_type(&ah_type, AF_INET); return -EAGAIN; } return 0; @@ -354,7 +354,7 @@ static void __exit ah4_fini(void) { if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0) printk(KERN_INFO "ip ah close: can't remove protocol\n"); - if (xfrm_unregister_type(&ah_type) < 0) + if (xfrm_unregister_type(&ah_type, AF_INET) < 0) printk(KERN_INFO "ip ah close: can't remove xfrm type\n"); } diff --git a/net/ipv4/esp.c b/net/ipv4/esp.c index 58817c2374e8..5464dc0c94e9 100644 --- a/net/ipv4/esp.c +++ b/net/ipv4/esp.c @@ -91,8 +91,8 @@ int esp_output(struct sk_buff *skb) top_iph->ttl = iph->ttl; /* TTL disclosed */ top_iph->protocol = IPPROTO_ESP; top_iph->check = 0; - top_iph->saddr = x->props.saddr.xfrm4_addr; - top_iph->daddr = x->id.daddr.xfrm4_addr; + top_iph->saddr = x->props.saddr.a4; + top_iph->daddr = x->id.daddr.a4; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); } else { esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len); @@ -276,7 +276,7 @@ void esp4_err(struct sk_buff *skb, u32 info) skb->h.icmph->code != ICMP_FRAG_NEEDED) return; - x = xfrm4_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET); if (!x) return; printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n", @@ -405,13 +405,13 @@ static struct inet_protocol esp4_protocol = { int __init esp4_init(void) { SET_MODULE_OWNER(&esp_type); - if (xfrm_register_type(&esp_type) < 0) { + if (xfrm_register_type(&esp_type, AF_INET) < 0) { printk(KERN_INFO "ip esp init: can't add xfrm type\n"); return -EAGAIN; } if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) { printk(KERN_INFO "ip esp init: can't add protocol\n"); - xfrm_unregister_type(&esp_type); + xfrm_unregister_type(&esp_type, AF_INET); return -EAGAIN; } return 0; @@ -421,7 +421,7 @@ static void __exit esp4_fini(void) { if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0) printk(KERN_INFO "ip esp close: can't remove protocol\n"); - if (xfrm_unregister_type(&esp_type) < 0) + if (xfrm_unregister_type(&esp_type, AF_INET) < 0) printk(KERN_INFO "ip esp close: can't remove xfrm type\n"); } diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index ec94140ff2cf..f26d67ca69eb 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb) struct rtable *rt; /* Route we use */ struct ip_options * opt = &(IPCB(skb)->opt); - if (!xfrm_policy_check(NULL, XFRM_POLICY_FWD, skb)) + if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) goto drop; if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb)) @@ -82,7 +82,7 @@ int ip_forward(struct sk_buff *skb) if (iph->ttl <= 1) goto too_many_hops; - if (!xfrm_route_forward(skb)) + if (!xfrm4_route_forward(skb)) goto drop; iph = skb->nh.iph; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index d1079ca00743..6131e3babfca 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -236,7 +236,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) int ret; if (!ipprot->no_policy && - !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) { + !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); return 0; } @@ -248,7 +248,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) IP_INC_STATS_BH(IpInDelivers); } else { if (!raw_sk) { - if (xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) { + if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { IP_INC_STATS_BH(IpInUnknownProtos); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 96b160ea59e7..1afbef9cd6a7 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -251,7 +251,7 @@ static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb) int raw_rcv(struct sock *sk, struct sk_buff *skb) { - if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) { + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) { kfree_skb(skb); return NET_RX_DROP; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index bd3c55086671..a915ce6d5889 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2600,13 +2600,6 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset, #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_CLS_ROUTE */ -int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) -{ - int err = 0; - err = __ip_route_output_key((struct rtable**)dst, fl); - return err; -} - int __init ip_rt_init(void) { int i, order, goal, rc = 0; @@ -2688,7 +2681,6 @@ int __init ip_rt_init(void) ip_rt_gc_interval; add_timer(&rt_periodic_timer); - xfrm_dst_lookup_register(xfrm_dst_lookup, AF_INET); #ifdef CONFIG_PROC_FS if (rt_cache_proc_init()) goto out_enomem; @@ -2698,6 +2690,7 @@ int __init ip_rt_init(void) #endif #endif xfrm_init(); + xfrm4_init(); out: return rc; out_enomem: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 384ae4f412df..29c1012ad767 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1798,7 +1798,7 @@ process: if (sk->state == TCP_TIME_WAIT) goto do_time_wait; - if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; if (sk_filter(sk, skb, 0)) @@ -1820,7 +1820,7 @@ process: return ret; no_tcp_socket: - if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_it; if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { @@ -1840,7 +1840,7 @@ discard_and_relse: goto discard_it; do_time_wait: - if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_and_relse; if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a75c36ee477f..1be52464ff65 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -946,7 +946,7 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) /* * Charge it to the socket, dropping if the queue is full. */ - if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) { + if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) { kfree_skb(skb); return -1; } @@ -1077,7 +1077,7 @@ int udp_rcv(struct sk_buff *skb) return 0; } - if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) + if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; /* No socket. Drop packet silently, if checksum is wrong */ diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c new file mode 100644 index 000000000000..59f4f8fda8f3 --- /dev/null +++ b/net/ipv4/xfrm4_input.c @@ -0,0 +1,129 @@ +/* + * xfrm4_input.c + * + * Changes: + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific portion + * + */ + +#include <net/ip.h> +#include <net/xfrm.h> + +static kmem_cache_t *secpath_cachep; + +int xfrm4_rcv(struct sk_buff *skb) +{ + int err; + u32 spi, seq; + struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; + struct xfrm_state *x; + int xfrm_nr = 0; + int decaps = 0; + + if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) + goto drop; + + do { + struct iphdr *iph = skb->nh.iph; + + if (xfrm_nr == XFRM_MAX_DEPTH) + goto drop; + + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET); + if (x == NULL) + goto drop; + + spin_lock(&x->lock); + if (unlikely(x->km.state != XFRM_STATE_VALID)) + goto drop_unlock; + + if (x->props.replay_window && xfrm_replay_check(x, seq)) + goto drop_unlock; + + if (x->type->input(x, skb)) + goto drop_unlock; + + if (x->props.replay_window) + xfrm_replay_advance(x, seq); + + x->curlft.bytes += skb->len; + x->curlft.packets++; + + spin_unlock(&x->lock); + + xfrm_vec[xfrm_nr++] = x; + + iph = skb->nh.iph; + + if (x->props.mode) { + if (iph->protocol != IPPROTO_IPIP) + goto drop; + skb->nh.raw = skb->data; + iph = skb->nh.iph; + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + decaps = 1; + break; + } + + if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) + goto drop; + } while (!err); + + /* Allocate new secpath or COW existing one. */ + + if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { + kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep; + struct sec_path *sp; + sp = kmem_cache_alloc(pool, SLAB_ATOMIC); + if (!sp) + goto drop; + if (skb->sp) { + memcpy(sp, skb->sp, sizeof(struct sec_path)); + secpath_put(skb->sp); + } else { + sp->pool = pool; + sp->len = 0; + } + atomic_set(&sp->refcnt, 1); + skb->sp = sp; + } + if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) + goto drop; + + memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*)); + skb->sp->len += xfrm_nr; + + if (decaps) { + if (!(skb->dev->flags&IFF_LOOPBACK)) { + dst_release(skb->dst); + skb->dst = NULL; + } + netif_rx(skb); + return 0; + } else { + return -skb->nh.iph->protocol; + } + +drop_unlock: + spin_unlock(&x->lock); + xfrm_state_put(x); +drop: + while (--xfrm_nr >= 0) + xfrm_state_put(xfrm_vec[xfrm_nr]); + kfree_skb(skb); + return 0; +} + + +void __init xfrm4_input_init(void) +{ + secpath_cachep = kmem_cache_create("secpath4_cache", + sizeof(struct sec_path), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + + if (!secpath_cachep) + panic("IP: failed to allocate secpath4_cache\n"); +} + diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c new file mode 100644 index 000000000000..0266111f5591 --- /dev/null +++ b/net/ipv4/xfrm4_policy.c @@ -0,0 +1,277 @@ +/* + * xfrm4_policy.c + * + * Changes: + * Kazunori MIYAZAWA @USAGI + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific portion + * + */ + +#include <linux/config.h> +#include <net/xfrm.h> +#include <net/ip.h> + +extern struct dst_ops xfrm4_dst_ops; +extern struct xfrm_policy_afinfo xfrm4_policy_afinfo; + +static struct xfrm_type_map xfrm4_type_map = { .lock = RW_LOCK_UNLOCKED }; + +static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) +{ + return __ip_route_output_key((struct rtable**)dst, fl); +} + +/* Check that the bundle accepts the flow and its components are + * still valid. + */ + +static int __xfrm4_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl) +{ + do { + if (xdst->u.dst.ops != &xfrm4_dst_ops) + return 1; + + if (!xfrm_selector_match(&xdst->u.dst.xfrm->sel, fl, AF_INET)) + return 0; + if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID || + xdst->u.dst.path->obsolete > 0) + return 0; + xdst = (struct xfrm_dst*)xdst->u.dst.child; + } while (xdst); + return 0; +} + +static struct dst_entry * +__xfrm4_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy) +{ + struct dst_entry *dst; + + if (!fl->fl4_src) + fl->fl4_src = rt->rt_src; + if (!fl->fl4_dst) + fl->fl4_dst = rt->rt_dst; + read_lock_bh(&policy->lock); + for (dst = policy->bundles; dst; dst = dst->next) { + struct xfrm_dst *xdst = (struct xfrm_dst*)dst; + if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/ + xdst->u.rt.fl.fl4_dst == fl->fl4_dst && + xdst->u.rt.fl.fl4_src == fl->fl4_src && + __xfrm4_bundle_ok(xdst, fl)) { + dst_clone(dst); + break; + } + } + read_unlock_bh(&policy->lock); + return dst; +} + +/* Allocate chain of dst_entry's, attach known xfrm's, calculate + * all the metrics... Shortly, bundle a bundle. + */ + +static int +__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, + struct flowi *fl, struct dst_entry **dst_p) +{ + struct dst_entry *dst, *dst_prev; + struct rtable *rt0 = (struct rtable*)(*dst_p); + struct rtable *rt = rt0; + u32 remote = fl->fl4_dst; + u32 local = fl->fl4_src; + int i; + int err; + int header_len = 0; + int trailer_len = 0; + + dst = dst_prev = NULL; + + for (i = 0; i < nx; i++) { + struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); + + if (unlikely(dst1 == NULL)) { + err = -ENOBUFS; + goto error; + } + + dst1->xfrm = xfrm[i]; + if (!dst) + dst = dst1; + else { + dst_prev->child = dst1; + dst1->flags |= DST_NOHASH; + dst_clone(dst1); + } + dst_prev = dst1; + if (xfrm[i]->props.mode) { + remote = xfrm[i]->id.daddr.a4; + local = xfrm[i]->props.saddr.a4; + } + header_len += xfrm[i]->props.header_len; + trailer_len += xfrm[i]->props.trailer_len; + } + + if (remote != fl->fl4_dst) { + struct flowi fl_tunnel = { .nl_u = { .ip4_u = + { .daddr = remote, + .saddr = local } + } + }; + err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET); + if (err) + goto error; + } else { + dst_hold(&rt->u.dst); + } + dst_prev->child = &rt->u.dst; + for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { + struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; + x->u.rt.fl = *fl; + + dst_prev->dev = rt->u.dst.dev; + if (rt->u.dst.dev) + dev_hold(rt->u.dst.dev); + dst_prev->obsolete = -1; + dst_prev->flags |= DST_HOST; + dst_prev->lastuse = jiffies; + dst_prev->header_len = header_len; + dst_prev->trailer_len = trailer_len; + memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics)); + dst_prev->path = &rt->u.dst; + + /* Copy neighbout for reachability confirmation */ + dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); + dst_prev->input = rt->u.dst.input; + dst_prev->output = dst_prev->xfrm->type->output; + if (rt->peer) + atomic_inc(&rt->peer->refcnt); + x->u.rt.peer = rt->peer; + /* Sheit... I remember I did this right. Apparently, + * it was magically lost, so this code needs audit */ + x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); + x->u.rt.rt_type = rt->rt_type; + x->u.rt.rt_src = rt0->rt_src; + x->u.rt.rt_dst = rt0->rt_dst; + x->u.rt.rt_gateway = rt->rt_gateway; + x->u.rt.rt_spec_dst = rt0->rt_spec_dst; + header_len -= x->u.dst.xfrm->props.header_len; + trailer_len -= x->u.dst.xfrm->props.trailer_len; + } + *dst_p = dst; + return 0; + +error: + if (dst) + dst_free(dst); + return err; +} + +static void +_decode_session4(struct sk_buff *skb, struct flowi *fl) +{ + struct iphdr *iph = skb->nh.iph; + u8 *xprth = skb->nh.raw + iph->ihl*4; + + if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) { + switch (iph->protocol) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_SCTP: + if (pskb_may_pull(skb, xprth + 4 - skb->data)) { + u16 *ports = (u16 *)xprth; + + fl->uli_u.ports.sport = ports[0]; + fl->uli_u.ports.dport = ports[1]; + } + break; + + case IPPROTO_ESP: + if (pskb_may_pull(skb, xprth + 4 - skb->data)) { + u32 *ehdr = (u32 *)xprth; + + fl->uli_u.spi = ehdr[0]; + } + break; + + case IPPROTO_AH: + if (pskb_may_pull(skb, xprth + 8 - skb->data)) { + u32 *ah_hdr = (u32*)xprth; + + fl->uli_u.spi = ah_hdr[1]; + } + break; + + default: + fl->uli_u.spi = 0; + break; + }; + } else { + memset(fl, 0, sizeof(struct flowi)); + } + fl->proto = iph->protocol; + fl->fl4_dst = iph->daddr; + fl->fl4_src = iph->saddr; +} + +static inline int xfrm4_garbage_collect(void) +{ + read_lock(&xfrm4_policy_afinfo.lock); + xfrm4_policy_afinfo.garbage_collect(); + read_unlock(&xfrm4_policy_afinfo.lock); + return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2); +} + +static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) +{ + struct dst_entry *path = dst->path; + + if (mtu < 68 + dst->header_len) + return; + + path->ops->update_pmtu(path, mtu); +} + +struct dst_ops xfrm4_dst_ops = { + .family = AF_INET, + .protocol = __constant_htons(ETH_P_IP), + .gc = xfrm4_garbage_collect, + .update_pmtu = xfrm4_update_pmtu, + .gc_thresh = 1024, + .entry_size = sizeof(struct xfrm_dst), +}; + +struct xfrm_policy_afinfo xfrm4_policy_afinfo = { + .family = AF_INET, + .lock = RW_LOCK_UNLOCKED, + .type_map = &xfrm4_type_map, + .dst_ops = &xfrm4_dst_ops, + .dst_lookup = xfrm4_dst_lookup, + .find_bundle = __xfrm4_find_bundle, + .bundle_create = __xfrm4_bundle_create, + .decode_session = _decode_session4, +}; + +void __init xfrm4_policy_init(void) +{ + xfrm_policy_register_afinfo(&xfrm4_policy_afinfo); +} + +void __exit xfrm4_policy_fini(void) +{ + xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); +} + +void __init xfrm4_init(void) +{ + xfrm4_state_init(); + xfrm4_policy_init(); + xfrm4_input_init(); +} + +void __exit xfrm4_fini(void) +{ + //xfrm4_input_fini(); + xfrm4_policy_fini(); + xfrm4_state_fini(); +} + diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c new file mode 100644 index 000000000000..77be6d24917e --- /dev/null +++ b/net/ipv4/xfrm4_state.c @@ -0,0 +1,128 @@ +/* + * xfrm4_state.c + * + * Changes: + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific portion + * + */ + +#include <net/xfrm.h> +#include <linux/pfkeyv2.h> +#include <linux/ipsec.h> + +extern struct xfrm_state_afinfo xfrm4_state_afinfo; + +static void +__xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl, + struct xfrm_tmpl *tmpl, + xfrm_address_t *daddr, xfrm_address_t *saddr) +{ + x->sel.daddr.a4 = fl->fl4_dst; + x->sel.saddr.a4 = fl->fl4_src; + x->sel.dport = fl->uli_u.ports.dport; + x->sel.dport_mask = ~0; + x->sel.sport = fl->uli_u.ports.sport; + x->sel.sport_mask = ~0; + x->sel.prefixlen_d = 32; + x->sel.prefixlen_s = 32; + x->sel.proto = fl->proto; + x->sel.ifindex = fl->oif; + x->id = tmpl->id; + if (x->id.daddr.a4 == 0) + x->id.daddr.a4 = daddr->a4; + x->props.saddr = tmpl->saddr; + if (x->props.saddr.a4 == 0) + x->props.saddr.a4 = saddr->a4; + x->props.mode = tmpl->mode; + x->props.reqid = tmpl->reqid; + x->props.family = AF_INET; +} + +static struct xfrm_state * +__xfrm4_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto) +{ + unsigned h = __xfrm4_spi_hash(daddr, spi, proto); + struct xfrm_state *x; + + list_for_each_entry(x, xfrm4_state_afinfo.state_byspi+h, byspi) { + if (x->props.family == AF_INET && + spi == x->id.spi && + daddr->a4 == x->id.daddr.a4 && + proto == x->id.proto) { + atomic_inc(&x->refcnt); + return x; + } + } + return NULL; +} + +static struct xfrm_state * +__xfrm4_find_acq(u8 mode, u16 reqid, u8 proto, + xfrm_address_t *daddr, xfrm_address_t *saddr, + int create) +{ + struct xfrm_state *x, *x0; + unsigned h = __xfrm4_dst_hash(daddr); + + x0 = NULL; + + list_for_each_entry(x, xfrm4_state_afinfo.state_bydst+h, bydst) { + if (x->props.family == AF_INET && + daddr->a4 == x->id.daddr.a4 && + mode == x->props.mode && + proto == x->id.proto && + saddr->a4 == x->props.saddr.a4 && + reqid == x->props.reqid && + x->km.state == XFRM_STATE_ACQ) { + if (!x0) + x0 = x; + if (x->id.spi) + continue; + x0 = x; + break; + } + } + if (x0) { + atomic_inc(&x0->refcnt); + } else if (create && (x0 = xfrm_state_alloc()) != NULL) { + x0->sel.daddr.a4 = daddr->a4; + x0->sel.saddr.a4 = saddr->a4; + x0->sel.prefixlen_d = 32; + x0->sel.prefixlen_s = 32; + x0->props.saddr.a4 = saddr->a4; + x0->km.state = XFRM_STATE_ACQ; + x0->id.daddr.a4 = daddr->a4; + x0->id.proto = proto; + x0->props.family = AF_INET; + x0->props.mode = mode; + x0->props.reqid = reqid; + x0->props.family = AF_INET; + x0->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; + atomic_inc(&x0->refcnt); + mod_timer(&x0->timer, jiffies + XFRM_ACQ_EXPIRES*HZ); + atomic_inc(&x0->refcnt); + list_add_tail(&x0->bydst, xfrm4_state_afinfo.state_bydst+h); + wake_up(&km_waitq); + } + return x0; +} + +static struct xfrm_state_afinfo xfrm4_state_afinfo = { + .family = AF_INET, + .lock = RW_LOCK_UNLOCKED, + .init_tempsel = __xfrm4_init_tempsel, + .state_lookup = __xfrm4_state_lookup, + .find_acq = __xfrm4_find_acq, +}; + +void __init xfrm4_state_init(void) +{ + xfrm_state_register_afinfo(&xfrm4_state_afinfo); +} + +void __exit xfrm4_state_fini(void) +{ + xfrm_state_unregister_afinfo(&xfrm4_state_afinfo); +} + diff --git a/net/ipv4/xfrm_algo.c b/net/ipv4/xfrm_algo.c index 5ff82327c232..920e9dc667df 100644 --- a/net/ipv4/xfrm_algo.c +++ b/net/ipv4/xfrm_algo.c @@ -219,6 +219,36 @@ static struct xfrm_algo_desc ealg_list[] = { }, }; +static struct xfrm_algo_desc calg_list[] = { +{ + .name = "deflate", + .uinfo = { + .comp = { + .threshold = 90, + } + }, + .desc = { .sadb_alg_id = SADB_X_CALG_DEFLATE } +}, +{ + .name = "lzs", + .uinfo = { + .comp = { + .threshold = 90, + } + }, + .desc = { .sadb_alg_id = SADB_X_CALG_LZS } +}, +{ + .name = "lzjh", + .uinfo = { + .comp = { + .threshold = 50, + } + }, + .desc = { .sadb_alg_id = SADB_X_CALG_LZJH } +}, +}; + static inline int aalg_entries(void) { return sizeof(aalg_list) / sizeof(aalg_list[0]); @@ -229,6 +259,12 @@ static inline int ealg_entries(void) return sizeof(ealg_list) / sizeof(ealg_list[0]); } +static inline int calg_entries(void) +{ + return sizeof(calg_list) / sizeof(calg_list[0]); +} + +/* Todo: generic iterators */ struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id) { int i; @@ -259,6 +295,21 @@ struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id) return NULL; } +struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id) +{ + int i; + + for (i = 0; i < calg_entries(); i++) { + if (calg_list[i].desc.sadb_alg_id == alg_id) { + if (calg_list[i].available) + return &calg_list[i]; + else + break; + } + } + return NULL; +} + struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name) { int i; @@ -295,6 +346,24 @@ struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name) return NULL; } +struct xfrm_algo_desc *xfrm_calg_get_byname(char *name) +{ + int i; + + if (!name) + return NULL; + + for (i=0; i < calg_entries(); i++) { + if (strcmp(name, calg_list[i].name) == 0) { + if (calg_list[i].available) + return &calg_list[i]; + else + break; + } + } + return NULL; +} + struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx) { if (idx >= aalg_entries()) @@ -311,6 +380,14 @@ struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx) return &ealg_list[idx]; } +struct xfrm_algo_desc *xfrm_calg_get_byidx(unsigned int idx) +{ + if (idx >= calg_entries()) + return NULL; + + return &calg_list[idx]; +} + /* * Probe for the availability of crypto algorithms, and set the available * flag for any algorithms found on the system. This is typically called by @@ -334,6 +411,12 @@ void xfrm_probe_algs(void) if (ealg_list[i].available != status) ealg_list[i].available = status; } + + for (i = 0; i < calg_entries(); i++) { + status = crypto_alg_available(calg_list[i].name, 0); + if (calg_list[i].available != status) + calg_list[i].available = status; + } #endif } diff --git a/net/ipv4/xfrm_input.c b/net/ipv4/xfrm_input.c index 337677396020..71fac0bca4e7 100644 --- a/net/ipv4/xfrm_input.c +++ b/net/ipv4/xfrm_input.c @@ -1,29 +1,26 @@ -/* Changes +/* + * xfrm_input.c * - * Mitsuru KANDA @USAGI : IPv6 Support - * Kazunori MIYAZAWA @USAGI : - * YOSHIFUJI Hideaki @USAGI : - * Kunihiro Ishiguro : + * Changes: + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific portion * */ #include <net/ip.h> -#include <net/ipv6.h> #include <net/xfrm.h> -static kmem_cache_t *secpath_cachep; - void __secpath_destroy(struct sec_path *sp) { int i; for (i = 0; i < sp->len; i++) xfrm_state_put(sp->xvec[i]); - kmem_cache_free(secpath_cachep, sp); + kmem_cache_free(sp->pool, sp); } /* Fetch spi and seq frpm ipsec header */ -static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *seq) +int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) { int offset, offset_seq; @@ -39,156 +36,7 @@ static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *seq) case IPPROTO_COMP: if (!pskb_may_pull(skb, 4)) return -EINVAL; - *spi = *(u16*)(skb->h.raw + 2); - *seq = 0; - return 0; - default: - return 1; - } - - if (!pskb_may_pull(skb, 16)) - return -EINVAL; - - *spi = *(u32*)(skb->h.raw + offset); - *seq = *(u32*)(skb->h.raw + offset_seq); - return 0; -} - - - -int xfrm4_rcv(struct sk_buff *skb) -{ - int err; - u32 spi, seq; - struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; - struct xfrm_state *x; - int xfrm_nr = 0; - int decaps = 0; - - if ((err = xfrm_parse_spi(skb, &spi, &seq)) != 0) - goto drop; - - do { - struct iphdr *iph = skb->nh.iph; - - if (xfrm_nr == XFRM_MAX_DEPTH) - goto drop; - - x = xfrm4_state_lookup(iph->daddr, spi, iph->protocol); - if (x == NULL) - goto drop; - - spin_lock(&x->lock); - if (unlikely(x->km.state != XFRM_STATE_VALID)) - goto drop_unlock; - - if (x->props.replay_window && xfrm_replay_check(x, seq)) - goto drop_unlock; - - if (x->type->input(x, skb)) - goto drop_unlock; - - if (x->props.replay_window) - xfrm_replay_advance(x, seq); - - x->curlft.bytes += skb->len; - x->curlft.packets++; - - spin_unlock(&x->lock); - - xfrm_vec[xfrm_nr++] = x; - - iph = skb->nh.iph; - - if (x->props.mode) { - if (iph->protocol != IPPROTO_IPIP) - goto drop; - skb->nh.raw = skb->data; - iph = skb->nh.iph; - memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); - decaps = 1; - break; - } - - if ((err = xfrm_parse_spi(skb, &spi, &seq)) < 0) - goto drop; - } while (!err); - - /* Allocate new secpath or COW existing one. */ - - if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { - struct sec_path *sp; - sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC); - if (!sp) - goto drop; - if (skb->sp) { - memcpy(sp, skb->sp, sizeof(struct sec_path)); - secpath_put(skb->sp); - } else - sp->len = 0; - atomic_set(&sp->refcnt, 1); - skb->sp = sp; - } - if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) - goto drop; - - memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*)); - skb->sp->len += xfrm_nr; - - if (decaps) { - if (!(skb->dev->flags&IFF_LOOPBACK)) { - dst_release(skb->dst); - skb->dst = NULL; - } - netif_rx(skb); - return 0; - } else { - return -skb->nh.iph->protocol; - } - -drop_unlock: - spin_unlock(&x->lock); - xfrm_state_put(x); -drop: - while (--xfrm_nr >= 0) - xfrm_state_put(xfrm_vec[xfrm_nr]); - kfree_skb(skb); - return 0; -} - - -void __init xfrm_input_init(void) -{ - secpath_cachep = kmem_cache_create("secpath_cache", - sizeof(struct sec_path), - 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); - - if (!secpath_cachep) - panic("IP: failed to allocate secpath_cache\n"); -} - -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - -/* Fetch spi and seq frpm ipsec header */ - -static int xfrm6_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) -{ - int offset, offset_seq; - - switch (nexthdr) { - case IPPROTO_AH: - offset = offsetof(struct ip_auth_hdr, spi); - offset_seq = offsetof(struct ip_auth_hdr, seq_no); - break; - case IPPROTO_ESP: - offset = offsetof(struct ip_esp_hdr, spi); - offset_seq = offsetof(struct ip_esp_hdr, seq_no); - break; - case IPPROTO_COMP: - if (!pskb_may_pull(skb, 4)) - return -EINVAL; - *spi = *(u16*)(skb->h.raw + 2); + *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2))); *seq = 0; return 0; default: @@ -202,253 +50,3 @@ static int xfrm6_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) *seq = *(u32*)(skb->h.raw + offset_seq); return 0; } - -static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) -{ - u8 *opt = (u8 *)opthdr; - int len = ipv6_optlen(opthdr); - int off = 0; - int optlen = 0; - - off += 2; - len -= 2; - - while (len > 0) { - - switch (opt[off]) { - - case IPV6_TLV_PAD0: - optlen = 1; - break; - default: - if (len < 2) - goto bad; - optlen = opt[off+1]+2; - if (len < optlen) - goto bad; - if (opt[off] & 0x20) - memset(&opt[off+2], 0, opt[off+1]); - break; - } - - off += optlen; - len -= optlen; - } - if (len == 0) - return 1; - -bad: - return 0; -} - -int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir) -{ - u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - unsigned int packet_len = skb->tail - skb->nh.raw; - u8 nexthdr = skb->nh.ipv6h->nexthdr; - u8 nextnexthdr = 0; - - *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; - - while (offset + 1 <= packet_len) { - - switch (nexthdr) { - - case NEXTHDR_HOP: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun hopopts\n"); - return 0; - } - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_ROUTING: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0; - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_DEST: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun destopt\n"); - return 0; - } - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_AUTH: - if (dir == XFRM_POLICY_OUT) { - memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0, - (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2); - } - if (exthdr->nexthdr == NEXTHDR_DEST) { - offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - nextnexthdr = exthdr->nexthdr; - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun destopt\n"); - return 0; - } - } - return nexthdr; - default : - return nexthdr; - } - } - - return nexthdr; -} - -int xfrm6_rcv(struct sk_buff *skb) -{ - int err; - u32 spi, seq; - struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; - struct xfrm_state *x; - int xfrm_nr = 0; - int decaps = 0; - struct ipv6hdr *hdr = skb->nh.ipv6h; - unsigned char *tmp_hdr = NULL; - int hdr_len = 0; - u16 nh_offset = 0; - u8 nexthdr = 0; - - if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) { - nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; - hdr_len = sizeof(struct ipv6hdr); - } else { - hdr_len = skb->h.raw - skb->nh.raw; - } - - tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); - if (!tmp_hdr) - goto drop; - memcpy(tmp_hdr, skb->nh.raw, hdr_len); - - nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN); - hdr->priority = 0; - hdr->flow_lbl[0] = 0; - hdr->flow_lbl[1] = 0; - hdr->flow_lbl[2] = 0; - hdr->hop_limit = 0; - - if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) != 0) - goto drop; - - do { - struct ipv6hdr *iph = skb->nh.ipv6h; - - if (xfrm_nr == XFRM_MAX_DEPTH) - goto drop; - - x = xfrm6_state_lookup(&iph->daddr, spi, nexthdr); - if (x == NULL) - goto drop; - spin_lock(&x->lock); - if (unlikely(x->km.state != XFRM_STATE_VALID)) - goto drop_unlock; - - if (x->props.replay_window && xfrm_replay_check(x, seq)) - goto drop_unlock; - - nexthdr = x->type->input(x, skb); - if (nexthdr <= 0) - goto drop_unlock; - - if (x->props.replay_window) - xfrm_replay_advance(x, seq); - - x->curlft.bytes += skb->len; - x->curlft.packets++; - - spin_unlock(&x->lock); - - xfrm_vec[xfrm_nr++] = x; - - iph = skb->nh.ipv6h; /* ??? */ - - if (nexthdr == NEXTHDR_DEST) { - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { - err = -EINVAL; - goto drop; - } - nexthdr = skb->h.raw[0]; - nh_offset = skb->h.raw - skb->nh.raw; - skb_pull(skb, (skb->h.raw[1]+1)<<3); - skb->h.raw = skb->data; - } - - if (x->props.mode) { /* XXX */ - if (iph->nexthdr != IPPROTO_IPV6) - goto drop; - skb->nh.raw = skb->data; - iph = skb->nh.ipv6h; - decaps = 1; - break; - } - - if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) < 0) - goto drop; - } while (!err); - - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - skb->nh.raw[nh_offset] = nexthdr; - skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr)); - - /* Allocate new secpath or COW existing one. */ - if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { - struct sec_path *sp; - sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC); - if (!sp) - goto drop; - if (skb->sp) { - memcpy(sp, skb->sp, sizeof(struct sec_path)); - secpath_put(skb->sp); - } else - sp->len = 0; - atomic_set(&sp->refcnt, 1); - skb->sp = sp; - } - - if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) - goto drop; - - memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*)); - skb->sp->len += xfrm_nr; - - if (decaps) { - if (!(skb->dev->flags&IFF_LOOPBACK)) { - dst_release(skb->dst); - skb->dst = NULL; - } - netif_rx(skb); - return 0; - } else { - return -nexthdr; - } - -drop_unlock: - spin_unlock(&x->lock); - xfrm_state_put(x); -drop: - if (tmp_hdr) kfree(tmp_hdr); - while (--xfrm_nr >= 0) - xfrm_state_put(xfrm_vec[xfrm_nr]); - kfree_skb(skb); - return 0; -} - -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ diff --git a/net/ipv4/xfrm_policy.c b/net/ipv4/xfrm_policy.c index ed14bd106856..055b7fffa49d 100644 --- a/net/ipv4/xfrm_policy.c +++ b/net/ipv4/xfrm_policy.c @@ -1,16 +1,20 @@ -/* Changes +/* + * xfrm_policy.c * - * Mitsuru KANDA @USAGI : IPv6 Support - * Kazunori MIYAZAWA @USAGI : - * Kunihiro Ishiguro : + * Changes: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * IPv6 support + * Kazunori MIYAZAWA @USAGI + * YOSHIFUJI Hideaki + * Split up af-specific portion * */ #include <linux/config.h> #include <net/xfrm.h> #include <net/ip.h> -#include <net/ipv6.h> -#include <net/ip6_route.h> DECLARE_MUTEX(xfrm_cfg_sem); @@ -19,12 +23,10 @@ static rwlock_t xfrm_policy_lock = RW_LOCK_UNLOCKED; struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2]; -extern struct dst_ops xfrm4_dst_ops; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -extern struct dst_ops xfrm6_dst_ops; -#endif +static rwlock_t xfrm_policy_afinfo_lock = RW_LOCK_UNLOCKED; +static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO]; -static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family); +kmem_cache_t *xfrm_dst_cache; /* Limited flow cache. Its function now is to accelerate search for * policy rules. @@ -49,40 +51,8 @@ static kmem_cache_t *flow_cachep; struct flow_entry **flow_table; -#define FLOWCACHE_HASH_SIZE 1024 - -static inline u32 flow_hash(struct flowi *fl) -{ - u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport; - - hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4); - - hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport; - hash ^= (hash >> 10); - hash ^= (hash >> 20); - return hash & (FLOWCACHE_HASH_SIZE-1); -} - -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -static inline u32 flow_hash6(struct flowi *fl) -{ - u32 hash = fl->fl6_src->s6_addr32[2] ^ - fl->fl6_src->s6_addr32[3] ^ - fl->uli_u.ports.sport; - - hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4); - - hash ^= fl->fl6_dst->s6_addr32[2] ^ - fl->fl6_dst->s6_addr32[3] ^ - fl->uli_u.ports.dport; - hash ^= (hash >> 10); - hash ^= (hash >> 20); - return hash & (FLOWCACHE_HASH_SIZE-1); -} -#endif - -static int flow_lwm = 2*FLOWCACHE_HASH_SIZE; -static int flow_hwm = 4*FLOWCACHE_HASH_SIZE; +static int flow_lwm = 2*XFRM_FLOWCACHE_HASH_SIZE; +static int flow_hwm = 4*XFRM_FLOWCACHE_HASH_SIZE; static int flow_number[NR_CPUS] __cacheline_aligned; @@ -92,11 +62,11 @@ static void flow_cache_shrink(int cpu) { int i; struct flow_entry *fle, **flp; - int shrink_to = flow_lwm/FLOWCACHE_HASH_SIZE; + int shrink_to = flow_lwm/XFRM_FLOWCACHE_HASH_SIZE; - for (i=0; i<FLOWCACHE_HASH_SIZE; i++) { + for (i=0; i<XFRM_FLOWCACHE_HASH_SIZE; i++) { int k = 0; - flp = &flow_table[cpu*FLOWCACHE_HASH_SIZE+i]; + flp = &flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+i]; while ((fle=*flp) != NULL && k<shrink_to) { k++; flp = &fle->next; @@ -118,23 +88,12 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl, u32 hash; int cpu; - switch (family) { - case AF_INET: - hash = flow_hash(fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - hash = flow_hash6(fl); - break; -#endif - default: - return NULL; - } + hash = flow_hash(fl, family); local_bh_disable(); cpu = smp_processor_id(); - for (fle = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash]; + for (fle = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash]; fle; fle = fle->next) { if (memcmp(fl, &fle->fl, sizeof(fle->fl)) == 0 && fle->dir == dir) { @@ -172,8 +131,8 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl, fle->pol = pol; if (pol) atomic_inc(&pol->refcnt); - fle->next = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash]; - flow_table[cpu*FLOWCACHE_HASH_SIZE+hash] = fle; + fle->next = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash]; + flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash] = fle; } } local_bh_enable(); @@ -193,7 +152,7 @@ void __init flow_cache_init(void) panic("NET: failed to allocate flow cache slab\n"); for (order = 0; - (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*FLOWCACHE_HASH_SIZE); + (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*XFRM_FLOWCACHE_HASH_SIZE); order++) /* NOTHING */; @@ -205,130 +164,82 @@ void __init flow_cache_init(void) memset(flow_table, 0, PAGE_SIZE<<order); } -static struct xfrm_type *xfrm_type_map[256]; -static rwlock_t xfrm_type_lock = RW_LOCK_UNLOCKED; - -int xfrm_register_type(struct xfrm_type *type) +int xfrm_register_type(struct xfrm_type *type, unsigned short family) { + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + struct xfrm_type_map *typemap; int err = 0; - write_lock(&xfrm_type_lock); - if (xfrm_type_map[type->proto] == NULL) - xfrm_type_map[type->proto] = type; + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + typemap = afinfo->type_map; + + write_lock(&typemap->lock); + if (likely(typemap->map[type->proto] == NULL)) + typemap->map[type->proto] = type; else err = -EEXIST; - write_unlock(&xfrm_type_lock); + write_unlock(&typemap->lock); + xfrm_policy_put_afinfo(afinfo); return err; } -int xfrm_unregister_type(struct xfrm_type *type) +int xfrm_unregister_type(struct xfrm_type *type, unsigned short family) { + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + struct xfrm_type_map *typemap; int err = 0; - write_lock(&xfrm_type_lock); - if (xfrm_type_map[type->proto] != type) + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + typemap = afinfo->type_map; + + write_lock(&typemap->lock); + if (unlikely(typemap->map[type->proto] != type)) err = -ENOENT; else - xfrm_type_map[type->proto] = NULL; - write_unlock(&xfrm_type_lock); + typemap->map[type->proto] = NULL; + write_unlock(&typemap->lock); + xfrm_policy_put_afinfo(afinfo); return err; } -struct xfrm_type *xfrm_get_type(u8 proto) +struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) { + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + struct xfrm_type_map *typemap; struct xfrm_type *type; - read_lock(&xfrm_type_lock); - type = xfrm_type_map[proto]; - if (type && !try_module_get(type->owner)) + if (unlikely(afinfo == NULL)) + return NULL; + typemap = afinfo->type_map; + + read_lock(&typemap->lock); + type = typemap->map[proto]; + if (unlikely(type && !try_module_get(type->owner))) type = NULL; - read_unlock(&xfrm_type_lock); + read_unlock(&typemap->lock); + xfrm_policy_put_afinfo(afinfo); return type; } -static xfrm_dst_lookup_t *__xfrm_dst_lookup[AF_MAX]; -rwlock_t xdl_lock = RW_LOCK_UNLOCKED; - -int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup, - unsigned short family) +int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, + unsigned short family) { + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); int err = 0; - write_lock(&xdl_lock); - if (__xfrm_dst_lookup[family]) - err = -ENOBUFS; - else { - __xfrm_dst_lookup[family] = dst_lookup; - } - write_unlock(&xdl_lock); + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; - return err; -} - -void xfrm_dst_lookup_unregister(unsigned short family) -{ - write_lock(&xdl_lock); - if (__xfrm_dst_lookup[family]) - __xfrm_dst_lookup[family] = 0; - write_unlock(&xdl_lock); -} - -static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, - unsigned short family) -{ - int err = 0; - read_lock(&xdl_lock); - if (__xfrm_dst_lookup[family]) - err = __xfrm_dst_lookup[family](dst, fl); + if (likely(afinfo->dst_lookup != NULL)) + err = afinfo->dst_lookup(dst, fl); else err = -EINVAL; - read_unlock(&xdl_lock); + xfrm_policy_put_afinfo(afinfo); return err; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static struct xfrm_type *xfrm6_type_map[256]; -static rwlock_t xfrm6_type_lock = RW_LOCK_UNLOCKED; - -int xfrm6_register_type(struct xfrm_type *type) -{ - int err = 0; - - write_lock(&xfrm6_type_lock); - if (xfrm6_type_map[type->proto] == NULL) - xfrm6_type_map[type->proto] = type; - else - err = -EEXIST; - write_unlock(&xfrm6_type_lock); - return err; -} - -int xfrm6_unregister_type(struct xfrm_type *type) -{ - int err = 0; - - write_lock(&xfrm6_type_lock); - if (xfrm6_type_map[type->proto] != type) - err = -ENOENT; - else - xfrm6_type_map[type->proto] = NULL; - write_unlock(&xfrm6_type_lock); - return err; -} - -struct xfrm_type *xfrm6_get_type(u8 proto) -{ - struct xfrm_type *type; - - read_lock(&xfrm6_type_lock); - type = xfrm6_type_map[proto]; - if (type && !try_module_get(type->owner)) - type = NULL; - read_unlock(&xfrm6_type_lock); - return type; -} -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ - void xfrm_put_type(struct xfrm_type *type) { module_put(type->owner); @@ -608,18 +519,7 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, if (pol->family != family) continue; - switch (family) { - case AF_INET: - match = xfrm4_selector_match(sel, fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - match = xfrm6_selector_match(sel, fl); - break; -#endif - default: - match = 0; - } + match = xfrm_selector_match(sel, fl, family); if (match) { atomic_inc(&pol->refcnt); break; @@ -637,18 +537,7 @@ struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi if ((pol = sk->policy[dir]) != NULL) { int match; - switch (sk->family) { - case AF_INET: - match = xfrm4_selector_match(&pol->selector, fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - match = xfrm6_selector_match(&pol->selector, fl); - break; -#endif - default: - match = 0; - } + match = xfrm_selector_match(&pol->selector, fl, sk->family); if (match) atomic_inc(&pol->refcnt); else @@ -750,72 +639,27 @@ void __xfrm_sk_free_policy(struct xfrm_policy *pol, int dir) /* Resolve list of templates for the flow, given policy. */ static int -xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl, - struct xfrm_state **xfrm) +xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl, + struct xfrm_state **xfrm, + unsigned short family) { int nx; int i, error; - u32 daddr = fl->fl4_dst; - u32 saddr = fl->fl4_src; + xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family); + xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family); for (nx=0, i = 0; i < policy->xfrm_nr; i++) { struct xfrm_state *x; - u32 remote = daddr; - u32 local = saddr; - struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i]; - - if (tmpl->mode) { - remote = tmpl->id.daddr.xfrm4_addr; - local = tmpl->saddr.xfrm4_addr; - } - - x = xfrm4_state_find(remote, local, fl, tmpl, policy, &error); - - if (x && x->km.state == XFRM_STATE_VALID) { - xfrm[nx++] = x; - daddr = remote; - saddr = local; - continue; - } - if (x) { - error = (x->km.state == XFRM_STATE_ERROR ? - -EINVAL : -EAGAIN); - xfrm_state_put(x); - } - - if (!tmpl->optional) - goto fail; - } - return nx; - -fail: - for (nx--; nx>=0; nx--) - xfrm_state_put(xfrm[nx]); - return error; -} - -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -static int -xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl, - struct xfrm_state **xfrm) -{ - int nx; - int i, error; - struct in6_addr *daddr = fl->fl6_dst; - struct in6_addr *saddr = fl->fl6_src; - - for (nx=0, i = 0; i < policy->xfrm_nr; i++) { - struct xfrm_state *x=NULL; - struct in6_addr *remote = daddr; - struct in6_addr *local = saddr; + xfrm_address_t *remote = daddr; + xfrm_address_t *local = saddr; struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i]; if (tmpl->mode) { - remote = (struct in6_addr*)&tmpl->id.daddr; - local = (struct in6_addr*)&tmpl->saddr; + remote = &tmpl->id.daddr; + local = &tmpl->saddr; } - x = xfrm6_state_find(remote, local, fl, tmpl, policy, &error); + x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family); if (x && x->km.state == XFRM_STATE_VALID) { xfrm[nx++] = x; @@ -839,241 +683,40 @@ fail: xfrm_state_put(xfrm[nx]); return error; } -#endif /* Check that the bundle accepts the flow and its components are * still valid. */ -static int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl) +static struct dst_entry * +xfrm_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy, unsigned short family) { - do { - if (xdst->u.dst.ops != &xfrm4_dst_ops) - return 1; - - if (!xfrm4_selector_match(&xdst->u.dst.xfrm->sel, fl)) - return 0; - if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID || - xdst->u.dst.path->obsolete > 0) - return 0; - xdst = (struct xfrm_dst*)xdst->u.dst.child; - } while (xdst); - return 0; + struct dst_entry *x; + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + if (unlikely(afinfo == NULL)) + return ERR_PTR(-EINVAL); + x = afinfo->find_bundle(fl, rt, policy); + xfrm_policy_put_afinfo(afinfo); + return x; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static int xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl) -{ - do { - if (xdst->u.dst.ops != &xfrm6_dst_ops) - return 1; - - if (!xfrm6_selector_match(&xdst->u.dst.xfrm->sel, fl)) - return 0; - if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID || - xdst->u.dst.path->obsolete > 0) - return 0; - xdst = (struct xfrm_dst*)xdst->u.dst.child; - } while (xdst); - return 0; -} -#endif - - /* Allocate chain of dst_entry's, attach known xfrm's, calculate * all the metrics... Shortly, bundle a bundle. */ static int xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, - struct flowi *fl, struct dst_entry **dst_p) + struct flowi *fl, struct dst_entry **dst_p, + unsigned short family) { - struct dst_entry *dst, *dst_prev; - struct rtable *rt0 = (struct rtable*)(*dst_p); - struct rtable *rt = rt0; - u32 remote = fl->fl4_dst; - u32 local = fl->fl4_src; - int i; int err; - int header_len = 0; - int trailer_len = 0; - - dst = dst_prev = NULL; - - for (i = 0; i < nx; i++) { - struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); - - if (unlikely(dst1 == NULL)) { - err = -ENOBUFS; - goto error; - } - - dst1->xfrm = xfrm[i]; - if (!dst) - dst = dst1; - else { - dst_prev->child = dst1; - dst1->flags |= DST_NOHASH; - dst_hold(dst1); - } - dst_prev = dst1; - if (xfrm[i]->props.mode) { - remote = xfrm[i]->id.daddr.xfrm4_addr; - local = xfrm[i]->props.saddr.xfrm4_addr; - } - header_len += xfrm[i]->props.header_len; - trailer_len += xfrm[i]->props.trailer_len; - } - - if (remote != fl->fl4_dst) { - struct flowi fl_tunnel = { .nl_u = { .ip4_u = - { .daddr = remote, - .saddr = local } - } - }; - err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET); - if (err) - goto error; - } else { - dst_hold(&rt->u.dst); - } - dst_prev->child = &rt->u.dst; - for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { - struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; - x->u.rt.fl = *fl; - - dst_prev->dev = rt->u.dst.dev; - if (rt->u.dst.dev) - dev_hold(rt->u.dst.dev); - dst_prev->obsolete = -1; - dst_prev->flags |= DST_HOST; - dst_prev->lastuse = jiffies; - dst_prev->header_len = header_len; - dst_prev->trailer_len = trailer_len; - memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics)); - dst_prev->path = &rt->u.dst; - - /* Copy neighbout for reachability confirmation */ - dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); - dst_prev->input = rt->u.dst.input; - dst_prev->output = dst_prev->xfrm->type->output; - if (rt->peer) - atomic_inc(&rt->peer->refcnt); - x->u.rt.peer = rt->peer; - /* Sheit... I remember I did this right. Apparently, - * it was magically lost, so this code needs audit */ - x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); - x->u.rt.rt_type = rt->rt_type; - x->u.rt.rt_src = rt0->rt_src; - x->u.rt.rt_dst = rt0->rt_dst; - x->u.rt.rt_gateway = rt->rt_gateway; - x->u.rt.rt_spec_dst = rt0->rt_spec_dst; - header_len -= x->u.dst.xfrm->props.header_len; - trailer_len -= x->u.dst.xfrm->props.trailer_len; - } - *dst_p = dst; - return 0; - -error: - if (dst) - dst_free(dst); - return err; -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static int -xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, - struct flowi *fl, struct dst_entry **dst_p) -{ - struct dst_entry *dst, *dst_prev; - struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); - struct rt6_info *rt = rt0; - struct in6_addr *remote = fl->fl6_dst; - struct in6_addr *local = fl->fl6_src; - int i; - int err = 0; - int header_len = 0; - int trailer_len = 0; - - dst = dst_prev = NULL; - - for (i = 0; i < nx; i++) { - struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); - - if (unlikely(dst1 == NULL)) { - err = -ENOBUFS; - goto error; - } - - dst1->xfrm = xfrm[i]; - if (!dst) - dst = dst1; - else { - dst_prev->child = dst1; - dst1->flags |= DST_NOHASH; - dst_clone(dst1); - } - dst_prev = dst1; - if (xfrm[i]->props.mode) { - remote = (struct in6_addr*)&xfrm[i]->id.daddr; - local = (struct in6_addr*)&xfrm[i]->props.saddr; - } - header_len += xfrm[i]->props.header_len; - trailer_len += xfrm[i]->props.trailer_len; - } - - if (ipv6_addr_cmp(remote, fl->fl6_dst)) { - struct flowi fl_tunnel = { .nl_u = { .ip6_u = - { .daddr = remote, - .saddr = local } - } - }; - err = xfrm_dst_lookup((struct xfrm_dst**)&dst, &fl_tunnel, AF_INET6); - if (err) - goto error; - } else { - dst_clone(&rt->u.dst); - } - dst_prev->child = &rt->u.dst; - for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { - struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; - x->u.rt.fl = *fl; - - dst_prev->dev = rt->u.dst.dev; - if (rt->u.dst.dev) - dev_hold(rt->u.dst.dev); - dst_prev->obsolete = -1; - dst_prev->flags |= DST_HOST; - dst_prev->lastuse = jiffies; - dst_prev->header_len = header_len; - dst_prev->trailer_len = trailer_len; - memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics)); - dst_prev->path = &rt->u.dst; - - /* Copy neighbout for reachability confirmation */ - dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); - dst_prev->input = rt->u.dst.input; - dst_prev->output = dst_prev->xfrm->type->output; - /* Sheit... I remember I did this right. Apparently, - * it was magically lost, so this code needs audit */ - x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); - x->u.rt6.rt6i_metric = rt0->rt6i_metric; - x->u.rt6.rt6i_node = rt0->rt6i_node; - x->u.rt6.rt6i_hoplimit = rt0->rt6i_hoplimit; - x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; - memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); - header_len -= x->u.dst.xfrm->props.header_len; - trailer_len -= x->u.dst.xfrm->props.trailer_len; - } - *dst_p = dst; - return 0; - -error: - if (dst) - dst_free(dst); + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + if (unlikely(afinfo == NULL)) + return -EINVAL; + err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p); + xfrm_policy_put_afinfo(afinfo); return err; } -#endif /* Main function: finds/creates a bundle for given flow. * @@ -1141,46 +784,17 @@ restart: * LATER: help from flow cache. It is optional, this * is required only for output policy. */ - if (family == AF_INET) { - read_lock_bh(&policy->lock); - for (dst = policy->bundles; dst; dst = dst->next) { - struct xfrm_dst *xdst = (struct xfrm_dst*)dst; - if (xdst->u.rt.fl.fl4_dst == fl->fl4_dst && - xdst->u.rt.fl.fl4_src == fl->fl4_src && - xdst->u.rt.fl.oif == fl->oif && - xfrm_bundle_ok(xdst, fl)) { - dst_clone(dst); - break; - } - } - read_unlock_bh(&policy->lock); - if (dst) - break; - nx = xfrm4_tmpl_resolve(policy, fl, xfrm); -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - } else if (family == AF_INET6) { - read_lock_bh(&policy->lock); - for (dst = policy->bundles; dst; dst = dst->next) { - struct xfrm_dst *xdst = (struct xfrm_dst*)dst; - if (!ipv6_addr_cmp(&xdst->u.rt6.rt6i_dst.addr, fl->fl6_dst) && - !ipv6_addr_cmp(&xdst->u.rt6.rt6i_src.addr, fl->fl6_src) && - xfrm6_bundle_ok(xdst, fl)) { - dst_clone(dst); - break; - } - } - read_unlock_bh(&policy->lock); - if (dst) - break; - nx = xfrm6_tmpl_resolve(policy, fl, xfrm); -#endif - } else { - return -EINVAL; + dst = xfrm_find_bundle(fl, rt, policy, family); + if (IS_ERR(dst)) { + xfrm_pol_put(policy); + return PTR_ERR(dst); } if (dst) break; + nx = xfrm_tmpl_resolve(policy, fl, xfrm, family); + if (unlikely(nx<0)) { err = nx; if (err == -EAGAIN) { @@ -1191,18 +805,7 @@ restart: __set_task_state(tsk, TASK_INTERRUPTIBLE); add_wait_queue(&km_waitq, &wait); - switch (family) { - case AF_INET: - err = xfrm4_tmpl_resolve(policy, fl, xfrm); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - err = xfrm6_tmpl_resolve(policy, fl, xfrm); - break; -#endif - default: - err = -EINVAL; - } + err = xfrm_tmpl_resolve(policy, fl, xfrm, family); if (err == -EAGAIN) schedule(); __set_task_state(tsk, TASK_RUNNING); @@ -1225,19 +828,8 @@ restart: } dst = &rt->u.dst; - switch (family) { - case AF_INET: - err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - err = xfrm6_bundle_create(policy, xfrm, nx, fl, &dst); - break; -#endif - default: - err = -EINVAL; - } - + err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family); + if (unlikely(err)) { int i; for (i=0; i<nx; i++) @@ -1282,137 +874,39 @@ error: */ static inline int -xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x) -{ - return x->id.proto == tmpl->id.proto && - (x->id.spi == tmpl->id.spi || !tmpl->id.spi) && - x->props.mode == tmpl->mode && - (tmpl->aalgos & (1<<x->props.aalgo)) && - (!x->props.mode || !tmpl->saddr.xfrm4_addr || - tmpl->saddr.xfrm4_addr == x->props.saddr.xfrm4_addr); -} - -static inline int -xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx) -{ - for (; idx < sp->len; idx++) { - if (xfrm_state_ok(tmpl, sp->xvec[idx])) - return ++idx; - } - return -1; -} - -static void -_decode_session4(struct sk_buff *skb, struct flowi *fl) -{ - struct iphdr *iph = skb->nh.iph; - u8 *xprth = skb->nh.raw + iph->ihl*4; - - if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) { - switch (iph->protocol) { - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_SCTP: - if (pskb_may_pull(skb, xprth + 4 - skb->data)) { - u16 *ports = (u16 *)xprth; - - fl->uli_u.ports.sport = ports[0]; - fl->uli_u.ports.dport = ports[1]; - } - break; - - case IPPROTO_ESP: - if (pskb_may_pull(skb, xprth + 4 - skb->data)) { - u32 *ehdr = (u32 *)xprth; - - fl->uli_u.spi = ehdr[0]; - } - break; - - case IPPROTO_AH: - if (pskb_may_pull(skb, xprth + 8 - skb->data)) { - u32 *ah_hdr = (u32*)xprth; - - fl->uli_u.spi = ah_hdr[1]; - } - break; - - default: - fl->uli_u.spi = 0; - break; - }; - } else { - memset(fl, 0, sizeof(struct flowi)); - } - fl->proto = iph->protocol; - fl->fl4_dst = iph->daddr; - fl->fl4_src = iph->saddr; -} - -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -static inline int -xfrm6_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x) +xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x, + unsigned short family) { return x->id.proto == tmpl->id.proto && (x->id.spi == tmpl->id.spi || !tmpl->id.spi) && x->props.mode == tmpl->mode && (tmpl->aalgos & (1<<x->props.aalgo)) && - (!x->props.mode || !ipv6_addr_any((struct in6_addr*)&x->props.saddr) || - !ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr)); + !(x->props.mode && xfrm_state_addr_cmp(tmpl, x, family)); } static inline int -xfrm6_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx) +xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx, + unsigned short family) { for (; idx < sp->len; idx++) { - if (xfrm6_state_ok(tmpl, sp->xvec[idx])) + if (xfrm_state_ok(tmpl, sp->xvec[idx], family)) return ++idx; } return -1; } -static inline void -_decode_session6(struct sk_buff *skb, struct flowi *fl) +static int +_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family) { - u16 offset = sizeof(struct ipv6hdr); - struct ipv6hdr *hdr = skb->nh.ipv6h; - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - u8 nexthdr = skb->nh.ipv6h->nexthdr; - - fl->fl6_dst = &hdr->daddr; - fl->fl6_src = &hdr->saddr; - - while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) { - switch (nexthdr) { - case NEXTHDR_ROUTING: - case NEXTHDR_HOP: - case NEXTHDR_DEST: - offset += ipv6_optlen(exthdr); - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_SCTP: - if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) { - u16 *ports = (u16 *)exthdr; + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; - fl->uli_u.ports.sport = ports[0]; - fl->uli_u.ports.dport = ports[1]; - } - return; - - /* XXX Why are there these headers? */ - case IPPROTO_AH: - case IPPROTO_ESP: - default: - fl->uli_u.spi = 0; - return; - }; - } + afinfo->decode_session(skb, fl); + xfrm_policy_put_afinfo(afinfo); + return 0; } -#endif int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family) @@ -1420,38 +914,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, struct xfrm_policy *pol; struct flowi fl; - switch (family) { - case AF_INET: - _decode_session4(skb, &fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - _decode_session6(skb, &fl); - break; -#endif - default : + if (_decode_session(skb, &fl, family) < 0) return 0; - } /* First, check used SA against their selectors. */ if (skb->sp) { int i; for (i=skb->sp->len-1; i>=0; i--) { - int match; - switch (family) { - case AF_INET: - match = xfrm4_selector_match(&skb->sp->xvec[i]->sel, &fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - match = xfrm6_selector_match(&skb->sp->xvec[i]->sel, &fl); - break; -#endif - default: - match = 0; - } - if (!match) + if (!xfrm_selector_match(&skb->sp->xvec[i]->sel, &fl, family)) return 0; } } @@ -1483,20 +954,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * are implied between each two transformations. */ for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) { - if (pol->xfrm_vec[i].optional) - continue; - switch (family) { - case AF_INET: - k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - k = xfrm6_policy_ok(pol->xfrm_vec+i, sp, k); - break; -#endif - default: - k = -1; - } + k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family); if (k < 0) goto reject; } @@ -1514,18 +972,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct flowi fl; - switch (family) { - case AF_INET: - _decode_session4(skb, &fl); - break; -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - case AF_INET6: - _decode_session6(skb, &fl); - break; -#endif - default: + if (_decode_session(skb, &fl, family) < 0) return 0; - } return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0; } @@ -1603,20 +1051,6 @@ static void __xfrm_garbage_collect(void) } } -static inline int xfrm4_garbage_collect(void) -{ - __xfrm_garbage_collect(); - return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2); -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static inline int xfrm6_garbage_collect(void) -{ - __xfrm_garbage_collect(); - return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2); -} -#endif - static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x) { do { @@ -1660,29 +1094,6 @@ int xfrm_flush_bundles(struct xfrm_state *x) return 0; } - -static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) -{ - struct dst_entry *path = dst->path; - - if (mtu < 68 + dst->header_len) - return; - - path->ops->update_pmtu(path, mtu); -} - -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu) -{ - struct dst_entry *path = dst->path; - - if (mtu >= 1280 && mtu < dst_pmtu(dst)) - return; - - path->ops->update_pmtu(path, mtu); -} -#endif - /* Well... that's _TASK_. We need to scan through transformation * list and figure out what mss tcp should generate in order to * final datagram fit to mtu. Mama mia... :-) @@ -1723,52 +1134,99 @@ static int xfrm_get_mss(struct dst_entry *dst, u32 mtu) return res + dst->header_len; } -struct dst_ops xfrm4_dst_ops = { - .family = AF_INET, - .protocol = __constant_htons(ETH_P_IP), - .gc = xfrm4_garbage_collect, - .check = xfrm_dst_check, - .destroy = xfrm_dst_destroy, - .negative_advice = xfrm_negative_advice, - .link_failure = xfrm_link_failure, - .update_pmtu = xfrm4_update_pmtu, - .get_mss = xfrm_get_mss, - .gc_thresh = 1024, - .entry_size = sizeof(struct xfrm_dst), -}; +int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) +{ + int err = 0; + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + write_lock(&xfrm_policy_afinfo_lock); + if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL)) + err = -ENOBUFS; + else { + struct dst_ops *dst_ops = afinfo->dst_ops; + if (likely(dst_ops->kmem_cachep == NULL)) + dst_ops->kmem_cachep = xfrm_dst_cache; + if (likely(dst_ops->check == NULL)) + dst_ops->check = xfrm_dst_check; + if (likely(dst_ops->destroy == NULL)) + dst_ops->destroy = xfrm_dst_destroy; + if (likely(dst_ops->negative_advice == NULL)) + dst_ops->negative_advice = xfrm_negative_advice; + if (likely(dst_ops->link_failure == NULL)) + dst_ops->link_failure = xfrm_link_failure; + if (likely(dst_ops->get_mss == NULL)) + dst_ops->get_mss = xfrm_get_mss; + if (likely(afinfo->garbage_collect == NULL)) + afinfo->garbage_collect = __xfrm_garbage_collect; + xfrm_policy_afinfo[afinfo->family] = afinfo; + } + write_unlock(&xfrm_policy_afinfo_lock); + return err; +} -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -struct dst_ops xfrm6_dst_ops = { - .family = AF_INET6, - .protocol = __constant_htons(ETH_P_IPV6), - .gc = xfrm6_garbage_collect, - .check = xfrm_dst_check, - .destroy = xfrm_dst_destroy, - .negative_advice = xfrm_negative_advice, - .link_failure = xfrm_link_failure, - .update_pmtu = xfrm6_update_pmtu, - .get_mss = xfrm_get_mss, - .gc_thresh = 1024, - .entry_size = sizeof(struct xfrm_dst), -}; -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ +int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo) +{ + int err = 0; + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + write_lock(&xfrm_policy_afinfo_lock); + if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) { + if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo)) + err = -EINVAL; + else { + struct dst_ops *dst_ops = afinfo->dst_ops; + xfrm_policy_afinfo[afinfo->family] = NULL; + dst_ops->kmem_cachep = NULL; + dst_ops->check = NULL; + dst_ops->destroy = NULL; + dst_ops->negative_advice = NULL; + dst_ops->link_failure = NULL; + dst_ops->get_mss = NULL; + afinfo->garbage_collect = NULL; + } + } + write_unlock(&xfrm_policy_afinfo_lock); + return err; +} -void __init xfrm_init(void) +struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) { - xfrm4_dst_ops.kmem_cachep = kmem_cache_create("xfrm4_dst_cache", - sizeof(struct xfrm_dst), - 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + struct xfrm_policy_afinfo *afinfo; + if (unlikely(family >= NPROTO)) + return NULL; + read_lock(&xfrm_policy_afinfo_lock); + afinfo = xfrm_policy_afinfo[family]; + if (likely(afinfo != NULL)) + read_lock(&afinfo->lock); + read_unlock(&xfrm_policy_afinfo_lock); + return afinfo; +} - if (!xfrm4_dst_ops.kmem_cachep) - panic("IP: failed to allocate xfrm4_dst_cache\n"); +void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) +{ + if (unlikely(afinfo == NULL)) + return; + read_unlock(&afinfo->lock); +} -#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep; -#endif +void __init xfrm_policy_init(void) +{ + xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache", + sizeof(struct xfrm_dst), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (!xfrm_dst_cache) + panic("XFRM: failed to allocate xfrm_dst_cache\n"); +} - flow_cache_init(); +void __init xfrm_init(void) +{ xfrm_state_init(); - xfrm_input_init(); + flow_cache_init(); + xfrm_policy_init(); } diff --git a/net/ipv4/xfrm_state.c b/net/ipv4/xfrm_state.c index 6f9b2693c696..fd3eabd6a598 100644 --- a/net/ipv4/xfrm_state.c +++ b/net/ipv4/xfrm_state.c @@ -1,15 +1,19 @@ -/* Changes +/* + * xfrm_state.c * - * Mitsuru KANDA @USAGI : IPv6 Support - * Kazunori MIYAZAWA @USAGI : - * Kunihiro Ishiguro : + * Changes: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * IPv6 support + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific functions * */ #include <net/xfrm.h> #include <linux/pfkeyv2.h> #include <linux/ipsec.h> -#include <net/ipv6.h> /* Each xfrm_state may be linked to two tables: @@ -20,8 +24,6 @@ static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED; -#define XFRM_DST_HSIZE 1024 - /* Hash table to find appropriate SA towards given target (endpoint * of tunnel or destination of transport mode) allowed by selector. * @@ -33,7 +35,8 @@ static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE]; DECLARE_WAIT_QUEUE_HEAD(km_waitq); -#define ACQ_EXPIRES 30 +static rwlock_t xfrm_state_afinfo_lock = RW_LOCK_UNLOCKED; +static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; static void __xfrm_state_delete(struct xfrm_state *x); @@ -214,138 +217,37 @@ restart: wake_up(&km_waitq); } -struct xfrm_state * -xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, - struct xfrm_policy *pol, int *err) +static int +xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, + struct xfrm_tmpl *tmpl, + xfrm_address_t *daddr, xfrm_address_t *saddr, + unsigned short family) { - unsigned h = ntohl(daddr); - struct xfrm_state *x; - int acquire_in_progress = 0; - int error = 0; - struct xfrm_state *best = NULL; - - h = (h ^ (h>>16)) % XFRM_DST_HSIZE; - - spin_lock_bh(&xfrm_state_lock); - list_for_each_entry(x, xfrm_state_bydst+h, bydst) { - if (x->props.family == AF_INET && - daddr == x->id.daddr.xfrm4_addr && - x->props.reqid == tmpl->reqid && - (saddr == x->props.saddr.xfrm4_addr || !saddr || !x->props.saddr.xfrm4_addr) && - tmpl->mode == x->props.mode && - tmpl->id.proto == x->id.proto) { - /* Resolution logic: - 1. There is a valid state with matching selector. - Done. - 2. Valid state with inappropriate selector. Skip. - - Entering area of "sysdeps". - - 3. If state is not valid, selector is temporary, - it selects only session which triggered - previous resolution. Key manager will do - something to install a state with proper - selector. - */ - if (x->km.state == XFRM_STATE_VALID) { - if (!xfrm4_selector_match(&x->sel, fl)) - continue; - if (!best || - best->km.dying > x->km.dying || - (best->km.dying == x->km.dying && - best->curlft.add_time < x->curlft.add_time)) - best = x; - } else if (x->km.state == XFRM_STATE_ACQ) { - acquire_in_progress = 1; - } else if (x->km.state == XFRM_STATE_ERROR || - x->km.state == XFRM_STATE_EXPIRED) { - if (xfrm4_selector_match(&x->sel, fl)) - error = 1; - } - } - } - - if (best) { - atomic_inc(&best->refcnt); - spin_unlock_bh(&xfrm_state_lock); - return best; - } - - x = NULL; - if (!error && !acquire_in_progress && - ((x = xfrm_state_alloc()) != NULL)) { - /* Initialize temporary selector matching only - * to current session. */ - - x->sel.daddr.xfrm4_addr = fl->fl4_dst; - x->sel.daddr.xfrm4_mask = ~0; - x->sel.saddr.xfrm4_addr = fl->fl4_src; - x->sel.saddr.xfrm4_mask = ~0; - x->sel.dport = fl->uli_u.ports.dport; - x->sel.dport_mask = ~0; - x->sel.sport = fl->uli_u.ports.sport; - x->sel.sport_mask = ~0; - x->sel.prefixlen_d = 32; - x->sel.prefixlen_s = 32; - x->sel.proto = fl->proto; - x->sel.ifindex = fl->oif; - x->id = tmpl->id; - if (x->id.daddr.xfrm4_addr == 0) - x->id.daddr.xfrm4_addr = daddr; - x->props.family = AF_INET; - x->props.saddr = tmpl->saddr; - if (x->props.saddr.xfrm4_addr == 0) - x->props.saddr.xfrm4_addr = saddr; - x->props.mode = tmpl->mode; - x->props.reqid = tmpl->reqid; - x->props.family = AF_INET; - - if (km_query(x, tmpl, pol) == 0) { - x->km.state = XFRM_STATE_ACQ; - list_add_tail(&x->bydst, xfrm_state_bydst+h); - atomic_inc(&x->refcnt); - if (x->id.spi) { - h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto); - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; - list_add(&x->byspi, xfrm_state_byspi+h); - atomic_inc(&x->refcnt); - } - x->lft.hard_add_expires_seconds = ACQ_EXPIRES; - atomic_inc(&x->refcnt); - mod_timer(&x->timer, ACQ_EXPIRES*HZ); - } else { - x->km.state = XFRM_STATE_DEAD; - xfrm_state_put(x); - x = NULL; - error = 1; - } - } - spin_unlock_bh(&xfrm_state_lock); - if (!x) - *err = acquire_in_progress ? -EAGAIN : - (error ? -ESRCH : -ENOMEM); - return x; + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + if (!afinfo) + return -1; + afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); + xfrm_state_put_afinfo(afinfo); + return 0; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct xfrm_state * -xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, - struct xfrm_policy *pol, int *err) +xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, + struct flowi *fl, struct xfrm_tmpl *tmpl, + struct xfrm_policy *pol, int *err, + unsigned short family) { - unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]); + unsigned h = xfrm_dst_hash(daddr, family); struct xfrm_state *x; int acquire_in_progress = 0; int error = 0; struct xfrm_state *best = NULL; - h = (h ^ (h>>16)) % XFRM_DST_HSIZE; - spin_lock_bh(&xfrm_state_lock); list_for_each_entry(x, xfrm_state_bydst+h, bydst) { - if (x->props.family == AF_INET6&& - !ipv6_addr_cmp(daddr, (struct in6_addr *)&x->id.daddr) && + if (x->props.family == family && x->props.reqid == tmpl->reqid && - (!ipv6_addr_cmp(saddr, (struct in6_addr *)&x->props.saddr)|| ipv6_addr_any(saddr)) && + xfrm_state_addr_check(x, daddr, saddr, family) && tmpl->mode == x->props.mode && tmpl->id.proto == x->id.proto) { /* Resolution logic: @@ -362,7 +264,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f selector. */ if (x->km.state == XFRM_STATE_VALID) { - if (!xfrm6_selector_match(&x->sel, fl)) + if (!xfrm_selector_match(&x->sel, fl, family)) continue; if (!best || best->km.dying > x->km.dying || @@ -373,7 +275,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f acquire_in_progress = 1; } else if (x->km.state == XFRM_STATE_ERROR || x->km.state == XFRM_STATE_EXPIRED) { - if (xfrm6_selector_match(&x->sel, fl)) + if (xfrm_selector_match(&x->sel, fl, family)) error = 1; } } @@ -384,45 +286,26 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f spin_unlock_bh(&xfrm_state_lock); return best; } + x = NULL; if (!error && !acquire_in_progress && ((x = xfrm_state_alloc()) != NULL)) { /* Initialize temporary selector matching only * to current session. */ - memcpy(&x->sel.daddr, fl->fl6_dst, sizeof(struct in6_addr)); - memcpy(&x->sel.saddr, fl->fl6_src, sizeof(struct in6_addr)); - x->sel.dport = fl->uli_u.ports.dport; - x->sel.dport_mask = ~0; - x->sel.sport = fl->uli_u.ports.sport; - x->sel.sport_mask = ~0; - x->sel.prefixlen_d = 128; - x->sel.prefixlen_s = 128; - x->sel.proto = fl->proto; - x->sel.ifindex = fl->oif; - x->id = tmpl->id; - if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) - memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); - memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); - if (ipv6_addr_any((struct in6_addr*)&x->props.saddr)) - memcpy(&x->props.saddr, &saddr, sizeof(x->sel.saddr)); - x->props.mode = tmpl->mode; - x->props.reqid = tmpl->reqid; - x->props.family = AF_INET6; + xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); if (km_query(x, tmpl, pol) == 0) { x->km.state = XFRM_STATE_ACQ; list_add_tail(&x->bydst, xfrm_state_bydst+h); atomic_inc(&x->refcnt); if (x->id.spi) { - struct in6_addr *addr = (struct in6_addr*)&x->id.daddr; - h = ntohl((addr->s6_addr32[2]^addr->s6_addr32[3])^x->id.spi^x->id.proto); - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); list_add(&x->byspi, xfrm_state_byspi+h); atomic_inc(&x->refcnt); } - x->lft.hard_add_expires_seconds = ACQ_EXPIRES; + x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; atomic_inc(&x->refcnt); - mod_timer(&x->timer, ACQ_EXPIRES*HZ); + mod_timer(&x->timer, XFRM_ACQ_EXPIRES*HZ); } else { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); @@ -436,36 +319,17 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f (error ? -ESRCH : -ENOMEM); return x; } -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ void xfrm_state_insert(struct xfrm_state *x) { - unsigned h = 0; - - switch (x->props.family) { - case AF_INET: - h = ntohl(x->id.daddr.xfrm4_addr); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]); - break; -#endif - default: - return; - } - - h = (h ^ (h>>16)) % XFRM_DST_HSIZE; + unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family); spin_lock_bh(&xfrm_state_lock); list_add(&x->bydst, xfrm_state_bydst+h); atomic_inc(&x->refcnt); - if (x->props.family == AF_INET) - h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto); - else - h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto); - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); + list_add(&x->byspi, xfrm_state_byspi+h); atomic_inc(&x->refcnt); @@ -487,7 +351,7 @@ int xfrm_state_check_expire(struct xfrm_state *x) if (x->curlft.bytes >= x->lft.hard_byte_limit || x->curlft.packets >= x->lft.hard_packet_limit) { km_expired(x); - if (!mod_timer(&x->timer, jiffies + ACQ_EXPIRES*HZ)) + if (!mod_timer(&x->timer, jiffies + XFRM_ACQ_EXPIRES*HZ)) atomic_inc(&x->refcnt); return -EINVAL; } @@ -512,158 +376,37 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) } struct xfrm_state * -xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto) +xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, + unsigned short family) { - unsigned h = ntohl(daddr^spi^proto); struct xfrm_state *x; - - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + if (!afinfo) + return NULL; spin_lock_bh(&xfrm_state_lock); - list_for_each_entry(x, xfrm_state_byspi+h, byspi) { - if (x->props.family == AF_INET && - spi == x->id.spi && - daddr == x->id.daddr.xfrm4_addr && - proto == x->id.proto) { - atomic_inc(&x->refcnt); - spin_unlock_bh(&xfrm_state_lock); - return x; - } - } + x = afinfo->state_lookup(daddr, spi, proto); spin_unlock_bh(&xfrm_state_lock); - return NULL; + xfrm_state_put_afinfo(afinfo); + return x; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct xfrm_state * -xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto) +xfrm_find_acq(u8 mode, u16 reqid, u8 proto, + xfrm_address_t *daddr, xfrm_address_t *saddr, + int create, unsigned short family) { - unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]^spi^proto); struct xfrm_state *x; - - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; - - spin_lock_bh(&xfrm_state_lock); - list_for_each_entry(x, xfrm_state_byspi+h, byspi) { - if (x->props.family == AF_INET6 && - spi == x->id.spi && - !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) && - proto == x->id.proto) { - atomic_inc(&x->refcnt); - spin_unlock_bh(&xfrm_state_lock); - return x; - } - } - spin_unlock_bh(&xfrm_state_lock); - return NULL; -} -#endif - -struct xfrm_state * -xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create) -{ - struct xfrm_state *x, *x0; - unsigned h = ntohl(daddr); - - h = (h ^ (h>>16)) % XFRM_DST_HSIZE; - x0 = NULL; - - spin_lock_bh(&xfrm_state_lock); - list_for_each_entry(x, xfrm_state_bydst+h, bydst) { - if (x->props.family == AF_INET && - daddr == x->id.daddr.xfrm4_addr && - mode == x->props.mode && - proto == x->id.proto && - saddr == x->props.saddr.xfrm4_addr && - reqid == x->props.reqid && - x->km.state == XFRM_STATE_ACQ) { - if (!x0) - x0 = x; - if (x->id.spi) - continue; - x0 = x; - break; - } - } - if (x0) { - atomic_inc(&x0->refcnt); - } else if (create && (x0 = xfrm_state_alloc()) != NULL) { - x0->sel.daddr.xfrm4_addr = daddr; - x0->sel.daddr.xfrm4_mask = ~0; - x0->sel.saddr.xfrm4_addr = saddr; - x0->sel.saddr.xfrm4_mask = ~0; - x0->sel.prefixlen_d = 32; - x0->sel.prefixlen_s = 32; - x0->props.saddr.xfrm4_addr = saddr; - x0->km.state = XFRM_STATE_ACQ; - x0->id.daddr.xfrm4_addr = daddr; - x0->id.proto = proto; - x0->props.mode = mode; - x0->props.reqid = reqid; - x0->props.family = AF_INET; - x0->lft.hard_add_expires_seconds = ACQ_EXPIRES; - atomic_inc(&x0->refcnt); - mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ); - atomic_inc(&x0->refcnt); - list_add_tail(&x0->bydst, xfrm_state_bydst+h); - wake_up(&km_waitq); - } - spin_unlock_bh(&xfrm_state_lock); - return x0; -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -struct xfrm_state * -xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create) -{ - struct xfrm_state *x, *x0; - unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]); - - h = (h ^ (h>>16)) % XFRM_DST_HSIZE; - x0 = NULL; + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + if (!afinfo) + return NULL; spin_lock_bh(&xfrm_state_lock); - list_for_each_entry(x, xfrm_state_bydst+h, bydst) { - if (x->props.family == AF_INET6 && - !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) && - mode == x->props.mode && - proto == x->id.proto && - !ipv6_addr_cmp(saddr, (struct in6_addr *)x->props.saddr.a6) && - reqid == x->props.reqid && - x->km.state == XFRM_STATE_ACQ) { - if (!x0) - x0 = x; - if (x->id.spi) - continue; - x0 = x; - break; - } - } - if (x0) { - atomic_inc(&x0->refcnt); - } else if (create && (x0 = xfrm_state_alloc()) != NULL) { - memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr)); - memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr)); - x0->sel.prefixlen_d = 128; - x0->sel.prefixlen_s = 128; - memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr)); - x0->km.state = XFRM_STATE_ACQ; - memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr)); - x0->id.proto = proto; - x0->props.family = AF_INET6; - x0->props.mode = mode; - x0->props.reqid = reqid; - x0->lft.hard_add_expires_seconds = ACQ_EXPIRES; - atomic_inc(&x0->refcnt); - mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ); - atomic_inc(&x0->refcnt); - list_add_tail(&x0->bydst, xfrm_state_bydst+h); - wake_up(&km_waitq); - } + x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create); spin_unlock_bh(&xfrm_state_lock); - return x0; + xfrm_state_put_afinfo(afinfo); + return x; } -#endif /* Silly enough, but I'm lazy to build resolution list */ @@ -697,18 +440,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) return; if (minspi == maxspi) { - switch(x->props.family) { - case AF_INET: - x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto); - break; -#endif - default: - x0 = NULL; - } + x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); if (x0) { xfrm_state_put(x0); return; @@ -720,18 +452,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) maxspi = ntohl(maxspi); for (h=0; h<maxspi-minspi+1; h++) { spi = minspi + net_random()%(maxspi-minspi+1); - switch(x->props.family) { - case AF_INET: - x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto); - break; -#endif - default: - x0 = NULL; - } + x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family); if (x0 == NULL) break; xfrm_state_put(x0); @@ -740,19 +461,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) } if (x->id.spi) { spin_lock_bh(&xfrm_state_lock); - switch(x->props.family) { - case AF_INET: - h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto); - break; -#endif - default: - h = 0; /* XXX */ - } - h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; + h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); list_add(&x->byspi, xfrm_state_byspi+h); atomic_inc(&x->refcnt); spin_unlock_bh(&xfrm_state_lock); @@ -845,18 +554,7 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl) for (i=0; i<n; i++) { int match; - switch(x[i]->props.family) { - case AF_INET: - match = xfrm4_selector_match(&x[i]->sel, fl); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - match = xfrm6_selector_match(&x[i]->sel, fl); - break; -#endif - default: - match = 0; - } + match = xfrm_selector_match(&x[i]->sel, fl, x[i]->props.family); if (!match) return -EINVAL; } @@ -958,6 +656,66 @@ int xfrm_unregister_km(struct xfrm_mgr *km) return 0; } +int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) +{ + int err = 0; + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + write_lock(&xfrm_state_afinfo_lock); + if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) + err = -ENOBUFS; + else { + afinfo->state_bydst = xfrm_state_bydst; + afinfo->state_byspi = xfrm_state_byspi; + xfrm_state_afinfo[afinfo->family] = afinfo; + } + write_unlock(&xfrm_state_afinfo_lock); + return err; +} + +int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) +{ + int err = 0; + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + write_lock(&xfrm_state_afinfo_lock); + if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { + if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) + err = -EINVAL; + else { + xfrm_state_afinfo[afinfo->family] = NULL; + afinfo->state_byspi = NULL; + afinfo->state_bydst = NULL; + } + } + write_unlock(&xfrm_state_afinfo_lock); + return err; +} + +struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family) +{ + struct xfrm_state_afinfo *afinfo; + if (unlikely(family >= NPROTO)) + return NULL; + read_lock(&xfrm_state_afinfo_lock); + afinfo = xfrm_state_afinfo[family]; + if (likely(afinfo != NULL)) + read_lock(&afinfo->lock); + read_unlock(&xfrm_state_afinfo_lock); + return afinfo; +} + +void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) +{ + if (unlikely(afinfo == NULL)) + return; + read_unlock(&afinfo->lock); +} + void __init xfrm_state_init(void) { int i; diff --git a/net/ipv4/xfrm_user.c b/net/ipv4/xfrm_user.c index 28a44311a9c5..1a1d5be1a16b 100644 --- a/net/ipv4/xfrm_user.c +++ b/net/ipv4/xfrm_user.c @@ -2,11 +2,11 @@ * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * - * Changes - * - * Mitsuru KANDA @USAGI : IPv6 Support - * Kazunori MIYAZAWA @USAGI : - * Kunihiro Ishiguro : + * Changes: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * IPv6 support * */ @@ -24,9 +24,6 @@ #include <linux/ipsec.h> #include <linux/init.h> #include <linux/security.h> -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -#include <linux/in6.h> -#endif #include <net/sock.h> #include <net/xfrm.h> @@ -191,19 +188,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, goto error; err = -ENOENT; - switch (x->props.family) { - case AF_INET: - x->type = xfrm_get_type(x->id.proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x->type = xfrm6_get_type(x->id.proto); - break; -#endif - default: - x->type = NULL; - break; - } + x->type = xfrm_get_type(x->id.proto, x->props.family); if (x->type == NULL) goto error; @@ -238,21 +223,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) if (!x) return err; - switch (x->props.family) { - case AF_INET: - x1 = xfrm4_state_lookup(x->props.saddr.xfrm4_addr, - x->id.spi, x->id.proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x1 = xfrm6_state_lookup((struct in6_addr*)x->props.saddr.a6, - x->id.spi, x->id.proto); - break; -#endif - default: - x1 = NULL; - break; - } + x1 = xfrm_state_lookup(&x->props.saddr, x->id.spi, x->id.proto, x->props.family); if (x1) { xfrm_state_put(x); xfrm_state_put(x1); @@ -269,19 +240,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) struct xfrm_state *x; struct xfrm_usersa_id *p = NLMSG_DATA(nlh); - switch (p->family) { - case AF_INET: - x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto); - break; -#endif - default: - x = NULL; - break; - } + x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family); if (x == NULL) return -ESRCH; @@ -399,19 +358,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) struct sk_buff *resp_skb; int err; - switch (p->family) { - case AF_INET: - x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto); - break; -#endif - default: - x = NULL; - break; - } + x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family); err = -ESRCH; if (x == NULL) goto out_noput; @@ -462,23 +409,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void ** err = verify_userspi_info(p); if (err) goto out_noput; - switch (p->info.family) { - case AF_INET: - x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, - p->info.sel.daddr.xfrm4_addr, - p->info.sel.saddr.xfrm4_addr, 1); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x = xfrm6_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, - (struct in6_addr*)p->info.sel.daddr.a6, - (struct in6_addr*)p->info.sel.saddr.a6, 1); - break; -#endif - default: - x = NULL; - break; - } + x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, + &p->info.sel.daddr, + &p->info.sel.saddr, 1, + p->info.family); err = -ENOENT; if (x == NULL) goto out_noput; @@ -1086,10 +1020,26 @@ struct xfrm_policy *xfrm_compile_policy(u16 family, int opt, struct xfrm_policy *xp; int nr; - if (opt != IP_XFRM_POLICY) { - *dir = -EOPNOTSUPP; + switch (family) { + case AF_INET: + if (opt != IP_XFRM_POLICY) { + *dir = -EOPNOTSUPP; + return NULL; + } + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + if (opt != IPV6_XFRM_POLICY) { + *dir = -EOPNOTSUPP; + return NULL; + } + break; +#endif + default: + *dir = -EINVAL; return NULL; } + *dir = -EINVAL; if (len < sizeof(*p) || diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 3fec5c7e0093..d3080035e6c9 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -8,7 +8,8 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ - ip6_flowlabel.o ipv6_syms.o + ip6_flowlabel.o ipv6_syms.o \ + xfrm6_policy.o xfrm6_state.o xfrm6_input.o obj-$(CONFIG_INET6_AH) += ah6.o obj-$(CONFIG_INET6_ESP) += esp6.o diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 468d94db3d29..e35069d8e75d 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -228,7 +228,7 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, type != ICMPV6_PKT_TOOBIG) return; - x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6); if (!x) return; @@ -336,14 +336,14 @@ int __init ah6_init(void) { SET_MODULE_OWNER(&ah6_type); - if (xfrm6_register_type(&ah6_type) < 0) { + if (xfrm_register_type(&ah6_type, AF_INET6) < 0) { printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n"); return -EAGAIN; } if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { printk(KERN_INFO "ipv6 ah init: can't add protocol\n"); - xfrm6_unregister_type(&ah6_type); + xfrm_unregister_type(&ah6_type, AF_INET6); return -EAGAIN; } @@ -355,7 +355,7 @@ static void __exit ah6_fini(void) if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) printk(KERN_INFO "ipv6 ah close: can't remove protocol\n"); - if (xfrm6_unregister_type(&ah6_type) < 0) + if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0) printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n"); } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 8dc3c0ebc083..19450a5025d2 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -377,7 +377,7 @@ void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, type != ICMPV6_PKT_TOOBIG) return; - x = xfrm6_state_lookup(&iph->daddr, esph->spi, IPPROTO_ESP); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6); if (!x) return; printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/" @@ -504,13 +504,13 @@ static struct inet6_protocol esp6_protocol = { int __init esp6_init(void) { SET_MODULE_OWNER(&esp6_type); - if (xfrm6_register_type(&esp6_type) < 0) { + if (xfrm_register_type(&esp6_type, AF_INET6) < 0) { printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n"); return -EAGAIN; } if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) { printk(KERN_INFO "ipv6 esp init: can't add protocol\n"); - xfrm6_unregister_type(&esp6_type); + xfrm_unregister_type(&esp6_type, AF_INET6); return -EAGAIN; } @@ -521,7 +521,7 @@ static void __exit esp6_fini(void) { if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0) printk(KERN_INFO "ipv6 esp close: can't remove protocol\n"); - if (xfrm6_unregister_type(&esp6_type) < 0) + if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0) printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n"); } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5400e7e17af8..02206d65a768 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -643,7 +643,8 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, if (flags&MSG_PROBE) goto out; /* alloc skb with mtu as we do in the IPv4 stack for IPsec */ - skb = sock_alloc_send_skb(sk, mtu, flags & MSG_DONTWAIT, &err); + skb = sock_alloc_send_skb(sk, mtu + LL_RESERVED_SPACE(dev), + flags & MSG_DONTWAIT, &err); if (skb == NULL) { IP6_INC_STATS(Ip6OutDiscards); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index cf1356a96514..9576a3a03800 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -47,6 +47,7 @@ #include <net/inet_common.h> #include <net/tcp.h> #include <net/udp.h> +#include <net/xfrm.h> #include <asm/uaccess.h> @@ -404,6 +405,10 @@ done: case IPV6_FLOWLABEL_MGR: retv = ipv6_flowlabel_opt(sk, optval, optlen); break; + case IPV6_IPSEC_POLICY: + case IPV6_XFRM_POLICY: + retv = xfrm_user_policy(sk, optname, optval, optlen); + break; #ifdef CONFIG_NETFILTER default: diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c index e811463ebfb7..d8ead93cc7a2 100644 --- a/net/ipv6/ipv6_syms.c +++ b/net/ipv6/ipv6_syms.c @@ -5,6 +5,7 @@ #include <net/ipv6.h> #include <net/addrconf.h> #include <net/ip6_route.h> +#include <net/xfrm.h> EXPORT_SYMBOL(ipv6_addr_type); EXPORT_SYMBOL(icmpv6_send); @@ -31,3 +32,5 @@ EXPORT_SYMBOL(ipv6_get_saddr); EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(in6addr_any); EXPORT_SYMBOL(in6addr_loopback); +EXPORT_SYMBOL(xfrm6_rcv); +EXPORT_SYMBOL(xfrm6_clear_mutable_options); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d0a19947ebf7..85c6fe4fd0d9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1867,15 +1867,6 @@ ctl_table ipv6_route_table[] = { #endif -int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) -{ - int err = 0; - *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl); - if (!*dst) - err = -ENETUNREACH; - return err; -} - void __init ip6_route_init(void) { ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache", @@ -1883,11 +1874,11 @@ void __init ip6_route_init(void) 0, SLAB_HWCACHE_ALIGN, NULL, NULL); fib6_init(); - xfrm_dst_lookup_register(xfrm6_dst_lookup, AF_INET6); #ifdef CONFIG_PROC_FS proc_net_create("ipv6_route", 0, rt6_proc_info); proc_net_create("rt6_stats", 0, rt6_proc_stats); #endif + xfrm6_init(); } #ifdef MODULE @@ -1897,7 +1888,7 @@ void ip6_route_cleanup(void) proc_net_remove("ipv6_route"); proc_net_remove("rt6_stats"); #endif - xfrm_dst_lookup_unregister(AF_INET6); + xfrm6_fini(); rt6_ifdown(NULL); fib6_gc_cleanup(); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4ea38b81ce5d..180b321dc7d1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -967,7 +967,7 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, struct ipv6_pinfo *np = inet6_sk(sk); if (skb->ip_summed == CHECKSUM_HW) { - th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); + th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); skb->csum = offsetof(struct tcphdr, check); } else { th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, @@ -1642,7 +1642,7 @@ process: goto discard_and_relse; if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) - goto discard_it; + goto discard_and_relse; skb->dev = NULL; diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c new file mode 100644 index 000000000000..b498eef68161 --- /dev/null +++ b/net/ipv6/xfrm6_input.c @@ -0,0 +1,279 @@ +/* + * xfrm6_input.c: based on net/ipv4/xfrm4_input.c + * + * Authors: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * YOSHIFUJI Hideaki @USAGI + * IPv6 support + */ + +#include <net/ip.h> +#include <net/ipv6.h> +#include <net/xfrm.h> + +static kmem_cache_t *secpath_cachep; + +static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) +{ + u8 *opt = (u8 *)opthdr; + int len = ipv6_optlen(opthdr); + int off = 0; + int optlen = 0; + + off += 2; + len -= 2; + + while (len > 0) { + + switch (opt[off]) { + + case IPV6_TLV_PAD0: + optlen = 1; + break; + default: + if (len < 2) + goto bad; + optlen = opt[off+1]+2; + if (len < optlen) + goto bad; + if (opt[off] & 0x20) + memset(&opt[off+2], 0, opt[off+1]); + break; + } + + off += optlen; + len -= optlen; + } + if (len == 0) + return 1; + +bad: + return 0; +} + +int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir) +{ + u16 offset = sizeof(struct ipv6hdr); + struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + unsigned int packet_len = skb->tail - skb->nh.raw; + u8 nexthdr = skb->nh.ipv6h->nexthdr; + u8 nextnexthdr = 0; + + *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; + + while (offset + 1 <= packet_len) { + + switch (nexthdr) { + + case NEXTHDR_HOP: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun hopopts\n"); + return 0; + } + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_ROUTING: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0; + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_DEST: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun destopt\n"); + return 0; + } + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_AUTH: + if (dir == XFRM_POLICY_OUT) { + memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0, + (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2); + } + if (exthdr->nexthdr == NEXTHDR_DEST) { + offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + nextnexthdr = exthdr->nexthdr; + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun destopt\n"); + return 0; + } + } + return nexthdr; + default : + return nexthdr; + } + } + + return nexthdr; +} + +int xfrm6_rcv(struct sk_buff *skb) +{ + int err; + u32 spi, seq; + struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; + struct xfrm_state *x; + int xfrm_nr = 0; + int decaps = 0; + struct ipv6hdr *hdr = skb->nh.ipv6h; + unsigned char *tmp_hdr = NULL; + int hdr_len = 0; + u16 nh_offset = 0; + u8 nexthdr = 0; + + if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) { + nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; + hdr_len = sizeof(struct ipv6hdr); + } else { + hdr_len = skb->h.raw - skb->nh.raw; + } + + tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); + if (!tmp_hdr) + goto drop; + memcpy(tmp_hdr, skb->nh.raw, hdr_len); + + nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN); + hdr->priority = 0; + hdr->flow_lbl[0] = 0; + hdr->flow_lbl[1] = 0; + hdr->flow_lbl[2] = 0; + hdr->hop_limit = 0; + + if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) + goto drop; + + do { + struct ipv6hdr *iph = skb->nh.ipv6h; + + if (xfrm_nr == XFRM_MAX_DEPTH) + goto drop; + + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr, AF_INET6); + if (x == NULL) + goto drop; + spin_lock(&x->lock); + if (unlikely(x->km.state != XFRM_STATE_VALID)) + goto drop_unlock; + + if (x->props.replay_window && xfrm_replay_check(x, seq)) + goto drop_unlock; + + nexthdr = x->type->input(x, skb); + if (nexthdr <= 0) + goto drop_unlock; + + if (x->props.replay_window) + xfrm_replay_advance(x, seq); + + x->curlft.bytes += skb->len; + x->curlft.packets++; + + spin_unlock(&x->lock); + + xfrm_vec[xfrm_nr++] = x; + + iph = skb->nh.ipv6h; /* ??? */ + + if (nexthdr == NEXTHDR_DEST) { + if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || + !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { + err = -EINVAL; + goto drop; + } + nexthdr = skb->h.raw[0]; + nh_offset = skb->h.raw - skb->nh.raw; + skb_pull(skb, (skb->h.raw[1]+1)<<3); + skb->h.raw = skb->data; + } + + if (x->props.mode) { /* XXX */ + if (iph->nexthdr != IPPROTO_IPV6) + goto drop; + skb->nh.raw = skb->data; + iph = skb->nh.ipv6h; + decaps = 1; + break; + } + + if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0) + goto drop; + } while (!err); + + memcpy(skb->nh.raw, tmp_hdr, hdr_len); + skb->nh.raw[nh_offset] = nexthdr; + skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr)); + + /* Allocate new secpath or COW existing one. */ + if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { + kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep; + struct sec_path *sp; + sp = kmem_cache_alloc(pool, SLAB_ATOMIC); + if (!sp) + goto drop; + if (skb->sp) { + memcpy(sp, skb->sp, sizeof(struct sec_path)); + secpath_put(skb->sp); + } else { + sp->pool = pool; + sp->len = 0; + } + atomic_set(&sp->refcnt, 1); + skb->sp = sp; + } + + if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) + goto drop; + + memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*)); + skb->sp->len += xfrm_nr; + + if (decaps) { + if (!(skb->dev->flags&IFF_LOOPBACK)) { + dst_release(skb->dst); + skb->dst = NULL; + } + netif_rx(skb); + return 0; + } else { + return -nexthdr; + } + +drop_unlock: + spin_unlock(&x->lock); + xfrm_state_put(x); +drop: + if (tmp_hdr) kfree(tmp_hdr); + while (--xfrm_nr >= 0) + xfrm_state_put(xfrm_vec[xfrm_nr]); + kfree_skb(skb); + return 0; +} + +void __init xfrm6_input_init(void) +{ + secpath_cachep = kmem_cache_create("secpath6_cache", + sizeof(struct sec_path), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + + if (!secpath_cachep) + panic("IPv6: failed to allocate secpath6_cache\n"); +} + diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c new file mode 100644 index 000000000000..9a31a2223998 --- /dev/null +++ b/net/ipv6/xfrm6_policy.c @@ -0,0 +1,273 @@ +/* + * xfrm6_policy.c: based on xfrm4_policy.c + * + * Authors: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * IPv6 support + * YOSHIFUJI Hideaki + * Split up af-specific portion + * + */ + +#include <linux/config.h> +#include <net/xfrm.h> +#include <net/ip.h> +#include <net/ipv6.h> +#include <net/ip6_route.h> + +extern struct dst_ops xfrm6_dst_ops; +extern struct xfrm_policy_afinfo xfrm6_policy_afinfo; + +static struct xfrm_type_map xfrm6_type_map = { .lock = RW_LOCK_UNLOCKED }; + +int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) +{ + int err = 0; + *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl); + if (!*dst) + err = -ENETUNREACH; + return err; +} + +/* Check that the bundle accepts the flow and its components are + * still valid. + */ + +static int __xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl) +{ + do { + if (xdst->u.dst.ops != &xfrm6_dst_ops) + return 1; + + if (!xfrm_selector_match(&xdst->u.dst.xfrm->sel, fl, AF_INET6)) + return 0; + if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID || + xdst->u.dst.path->obsolete > 0) + return 0; + xdst = (struct xfrm_dst*)xdst->u.dst.child; + } while (xdst); + return 0; +} + +static struct dst_entry * +__xfrm6_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy) +{ + struct dst_entry *dst; + + /* Still not clear if we should set fl->fl6_{src,dst}... */ + read_lock_bh(&policy->lock); + for (dst = policy->bundles; dst; dst = dst->next) { + struct xfrm_dst *xdst = (struct xfrm_dst*)dst; + if (!ipv6_addr_cmp(&xdst->u.rt6.rt6i_dst.addr, fl->fl6_dst) && + !ipv6_addr_cmp(&xdst->u.rt6.rt6i_src.addr, fl->fl6_src) && + __xfrm6_bundle_ok(xdst, fl)) { + dst_clone(dst); + break; + } + } + read_unlock_bh(&policy->lock); + return dst; +} + +/* Allocate chain of dst_entry's, attach known xfrm's, calculate + * all the metrics... Shortly, bundle a bundle. + */ + +static int +__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, + struct flowi *fl, struct dst_entry **dst_p) +{ + struct dst_entry *dst, *dst_prev; + struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); + struct rt6_info *rt = rt0; + struct in6_addr *remote = fl->fl6_dst; + struct in6_addr *local = fl->fl6_src; + int i; + int err = 0; + int header_len = 0; + int trailer_len = 0; + + dst = dst_prev = NULL; + + for (i = 0; i < nx; i++) { + struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); + + if (unlikely(dst1 == NULL)) { + err = -ENOBUFS; + goto error; + } + + dst1->xfrm = xfrm[i]; + if (!dst) + dst = dst1; + else { + dst_prev->child = dst1; + dst1->flags |= DST_NOHASH; + dst_clone(dst1); + } + dst_prev = dst1; + if (xfrm[i]->props.mode) { + remote = (struct in6_addr*)&xfrm[i]->id.daddr; + local = (struct in6_addr*)&xfrm[i]->props.saddr; + } + header_len += xfrm[i]->props.header_len; + trailer_len += xfrm[i]->props.trailer_len; + } + + if (ipv6_addr_cmp(remote, fl->fl6_dst)) { + struct flowi fl_tunnel = { .nl_u = { .ip6_u = + { .daddr = remote, + .saddr = local } + } + }; + err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET6); + if (err) + goto error; + } else { + dst_hold(&rt->u.dst); + } + dst_prev->child = &rt->u.dst; + for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { + struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; + x->u.rt.fl = *fl; + + dst_prev->dev = rt->u.dst.dev; + if (rt->u.dst.dev) + dev_hold(rt->u.dst.dev); + dst_prev->obsolete = -1; + dst_prev->flags |= DST_HOST; + dst_prev->lastuse = jiffies; + dst_prev->header_len = header_len; + dst_prev->trailer_len = trailer_len; + memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics)); + dst_prev->path = &rt->u.dst; + + /* Copy neighbout for reachability confirmation */ + dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); + dst_prev->input = rt->u.dst.input; + dst_prev->output = dst_prev->xfrm->type->output; + /* Sheit... I remember I did this right. Apparently, + * it was magically lost, so this code needs audit */ + x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); + x->u.rt6.rt6i_metric = rt0->rt6i_metric; + x->u.rt6.rt6i_node = rt0->rt6i_node; + x->u.rt6.rt6i_hoplimit = rt0->rt6i_hoplimit; + x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; + memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); + header_len -= x->u.dst.xfrm->props.header_len; + trailer_len -= x->u.dst.xfrm->props.trailer_len; + } + *dst_p = dst; + return 0; + +error: + if (dst) + dst_free(dst); + return err; +} + +static inline void +_decode_session6(struct sk_buff *skb, struct flowi *fl) +{ + u16 offset = sizeof(struct ipv6hdr); + struct ipv6hdr *hdr = skb->nh.ipv6h; + struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + u8 nexthdr = skb->nh.ipv6h->nexthdr; + + fl->fl6_dst = &hdr->daddr; + fl->fl6_src = &hdr->saddr; + + while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) { + switch (nexthdr) { + case NEXTHDR_ROUTING: + case NEXTHDR_HOP: + case NEXTHDR_DEST: + offset += ipv6_optlen(exthdr); + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_SCTP: + if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) { + u16 *ports = (u16 *)exthdr; + + fl->uli_u.ports.sport = ports[0]; + fl->uli_u.ports.dport = ports[1]; + } + return; + + /* XXX Why are there these headers? */ + case IPPROTO_AH: + case IPPROTO_ESP: + default: + fl->uli_u.spi = 0; + return; + }; + } +} + +static inline int xfrm6_garbage_collect(void) +{ + read_lock(&xfrm6_policy_afinfo.lock); + xfrm6_policy_afinfo.garbage_collect(); + read_unlock(&xfrm6_policy_afinfo.lock); + return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2); +} + +static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu) +{ + struct dst_entry *path = dst->path; + + if (mtu >= 1280 && mtu < dst_pmtu(dst)) + return; + + path->ops->update_pmtu(path, mtu); +} + +struct dst_ops xfrm6_dst_ops = { + .family = AF_INET6, + .protocol = __constant_htons(ETH_P_IPV6), + .gc = xfrm6_garbage_collect, + .update_pmtu = xfrm6_update_pmtu, + .gc_thresh = 1024, + .entry_size = sizeof(struct xfrm_dst), +}; + +struct xfrm_policy_afinfo xfrm6_policy_afinfo = { + .family = AF_INET6, + .lock = RW_LOCK_UNLOCKED, + .type_map = &xfrm6_type_map, + .dst_ops = &xfrm6_dst_ops, + .dst_lookup = xfrm6_dst_lookup, + .find_bundle = __xfrm6_find_bundle, + .bundle_create = __xfrm6_bundle_create, + .decode_session = _decode_session6, +}; + +void __init xfrm6_policy_init(void) +{ + xfrm_policy_register_afinfo(&xfrm6_policy_afinfo); +} + +void __exit xfrm6_policy_fini(void) +{ + xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); +} + +void __init xfrm6_init(void) +{ + xfrm6_policy_init(); + xfrm6_state_init(); + xfrm6_input_init(); +} + +void __exit xfrm6_fini(void) +{ + //xfrm6_input_fini(); + xfrm6_policy_fini(); + xfrm6_state_fini(); +} diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c new file mode 100644 index 000000000000..259ed5e0249b --- /dev/null +++ b/net/ipv6/xfrm6_state.c @@ -0,0 +1,134 @@ +/* + * xfrm6_state.c: based on xfrm4_state.c + * + * Authors: + * Mitsuru KANDA @USAGI + * Kazunori MIYAZAWA @USAGI + * Kunihiro Ishiguro + * IPv6 support + * YOSHIFUJI Hideaki @USAGI + * Split up af-specific portion + * + */ + +#include <net/xfrm.h> +#include <linux/pfkeyv2.h> +#include <linux/ipsec.h> +#include <net/ipv6.h> + +extern struct xfrm_state_afinfo xfrm6_state_afinfo; + +static void +__xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl, + struct xfrm_tmpl *tmpl, + xfrm_address_t *daddr, xfrm_address_t *saddr) +{ + /* Initialize temporary selector matching only + * to current session. */ + memcpy(&x->sel.daddr, fl->fl6_dst, sizeof(struct in6_addr)); + memcpy(&x->sel.saddr, fl->fl6_src, sizeof(struct in6_addr)); + x->sel.dport = fl->uli_u.ports.dport; + x->sel.dport_mask = ~0; + x->sel.sport = fl->uli_u.ports.sport; + x->sel.sport_mask = ~0; + x->sel.prefixlen_d = 128; + x->sel.prefixlen_s = 128; + x->sel.proto = fl->proto; + x->sel.ifindex = fl->oif; + x->id = tmpl->id; + if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) + memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); + memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); + if (ipv6_addr_any((struct in6_addr*)&x->props.saddr)) + memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr)); + x->props.mode = tmpl->mode; + x->props.reqid = tmpl->reqid; + x->props.family = AF_INET6; +} + +static struct xfrm_state * +__xfrm6_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto) +{ + unsigned h = __xfrm6_spi_hash(daddr, spi, proto); + struct xfrm_state *x; + + list_for_each_entry(x, xfrm6_state_afinfo.state_byspi+h, byspi) { + if (x->props.family == AF_INET6 && + spi == x->id.spi && + !ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)x->id.daddr.a6) && + proto == x->id.proto) { + atomic_inc(&x->refcnt); + return x; + } + } + return NULL; +} + +static struct xfrm_state * +__xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, + xfrm_address_t *daddr, xfrm_address_t *saddr, + int create) +{ + struct xfrm_state *x, *x0; + unsigned h = __xfrm6_dst_hash(daddr); + + x0 = NULL; + + list_for_each_entry(x, xfrm6_state_afinfo.state_bydst+h, bydst) { + if (x->props.family == AF_INET6 && + !ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)x->id.daddr.a6) && + mode == x->props.mode && + proto == x->id.proto && + !ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)x->props.saddr.a6) && + reqid == x->props.reqid && + x->km.state == XFRM_STATE_ACQ) { + if (!x0) + x0 = x; + if (x->id.spi) + continue; + x0 = x; + break; + } + } + if (x0) { + atomic_inc(&x0->refcnt); + } else if (create && (x0 = xfrm_state_alloc()) != NULL) { + memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr)); + memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr)); + x0->sel.prefixlen_d = 128; + x0->sel.prefixlen_s = 128; + memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr)); + x0->km.state = XFRM_STATE_ACQ; + memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr)); + x0->id.proto = proto; + x0->props.family = AF_INET6; + x0->props.mode = mode; + x0->props.reqid = reqid; + x0->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; + atomic_inc(&x0->refcnt); + mod_timer(&x0->timer, jiffies + XFRM_ACQ_EXPIRES*HZ); + atomic_inc(&x0->refcnt); + list_add_tail(&x0->bydst, xfrm6_state_afinfo.state_bydst+h); + wake_up(&km_waitq); + } + return x0; +} + +static struct xfrm_state_afinfo xfrm6_state_afinfo = { + .family = AF_INET6, + .lock = RW_LOCK_UNLOCKED, + .init_tempsel = __xfrm6_init_tempsel, + .state_lookup = __xfrm6_state_lookup, + .find_acq = __xfrm6_find_acq, +}; + +void __init xfrm6_state_init(void) +{ + xfrm_state_register_afinfo(&xfrm6_state_afinfo); +} + +void __exit xfrm6_state_fini(void) +{ + xfrm_state_unregister_afinfo(&xfrm6_state_afinfo); +} + diff --git a/net/key/af_key.c b/net/key/af_key.c index eb93b99b3379..257c153b4733 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -510,10 +510,8 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, { switch (((struct sockaddr*)(addr + 1))->sa_family) { case AF_INET: - xaddr->xfrm4_addr = + xaddr->a4 = ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr; - if (addr->sadb_address_prefixlen) - xaddr->xfrm4_mask = htonl(~0 << (32 - addr->sadb_address_prefixlen)); return AF_INET; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: @@ -530,10 +528,11 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs) { - struct xfrm_state *x; struct sadb_sa *sa; struct sadb_address *addr; uint16_t proto; + unsigned short family; + xfrm_address_t *xaddr; sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1]; if (sa == NULL) @@ -548,23 +547,24 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void ** if (addr == NULL) return NULL; - switch (((struct sockaddr *)(addr + 1))->sa_family) { + family = ((struct sockaddr *)(addr + 1))->sa_family; + switch (family) { case AF_INET: - x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr, - sa->sadb_sa_spi, proto); + xaddr = (xfrm_address_t *)&((struct sockaddr_in *)(addr + 1))->sin_addr; break; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: - x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr, - sa->sadb_sa_spi, proto); + xaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(addr + 1))->sin6_addr; break; #endif default: - x = NULL; - break; + xaddr = NULL; } - return x; + if (!xaddr) + return NULL; + + return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family); } #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1))) @@ -619,7 +619,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, /* identity & sensitivity */ if ((x->props.family == AF_INET && - x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) + x->sel.saddr.a4 != x->props.saddr.a4) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) || (x->props.family == AF_INET6 && memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr))) @@ -667,10 +667,17 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, sa->sadb_sa_auth = a ? a->desc.sadb_alg_id : 0; } sa->sadb_sa_encrypt = 0; + BUG_ON(x->ealg && x->calg); if (x->ealg) { struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name); sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0; } + /* KAME compatible: sadb_sa_encrypt is overloaded with calg id */ + if (x->calg) { + struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name); + sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0; + } + sa->sadb_sa_flags = 0; /* hard time */ @@ -724,7 +731,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr; + sin->sin_addr.s_addr = x->props.saddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -757,11 +764,11 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, if (x->props.family == AF_INET) { sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr; + sin->sin_addr.s_addr = x->id.daddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); - if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) { + if (x->sel.saddr.a4 != x->props.saddr.a4) { addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = @@ -775,7 +782,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr; + sin->sin_addr.s_addr = x->sel.saddr.a4; sin->sin_port = x->sel.sport; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -896,6 +903,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, Hence, we have to _ignore_ sadb_sa_state, which is also reasonable. */ if (sa->sadb_sa_auth > SADB_AALG_MAX || + (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP && + sa->sadb_sa_encrypt > SADB_X_CALG_MAX) || sa->sadb_sa_encrypt > SADB_EALG_MAX) return ERR_PTR(-EINVAL); key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1]; @@ -953,24 +962,35 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, x->props.aalgo = sa->sadb_sa_auth; /* x->algo.flags = sa->sadb_sa_flags; */ } - key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1]; if (sa->sadb_sa_encrypt) { - int keysize = 0; - struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt); - if (!a) - goto out; - if (key) - keysize = (key->sadb_key_bits + 7) / 8; - x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL); - if (!x->ealg) - goto out; - strcpy(x->ealg->alg_name, a->name); - x->ealg->alg_key_len = 0; - if (key) { - x->ealg->alg_key_len = key->sadb_key_bits; - memcpy(x->ealg->alg_key, key+1, keysize); + if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) { + struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt); + if (!a) + goto out; + x->calg = kmalloc(sizeof(*x->calg), GFP_KERNEL); + if (!x->calg) + goto out; + strcpy(x->calg->alg_name, a->name); + x->props.calgo = sa->sadb_sa_encrypt; + } else { + int keysize = 0; + struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt); + if (!a) + goto out; + key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1]; + if (key) + keysize = (key->sadb_key_bits + 7) / 8; + x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL); + if (!x->ealg) + goto out; + strcpy(x->ealg->alg_name, a->name); + x->ealg->alg_key_len = 0; + if (key) { + x->ealg->alg_key_len = key->sadb_key_bits; + memcpy(x->ealg->alg_key, key+1, keysize); + } + x->props.ealgo = sa->sadb_sa_encrypt; } - x->props.ealgo = sa->sadb_sa_encrypt; } /* x->algo.flags = sa->sadb_sa_flags; */ @@ -997,20 +1017,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, x->sel.prefixlen_s = addr->sadb_address_prefixlen; } - switch (x->props.family) { - case AF_INET: - x->type = xfrm_get_type(proto); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x->type = xfrm6_get_type(proto); - break; -#endif - default: - x->type = NULL; - break; - } - + x->type = xfrm_get_type(proto, x->props.family); if (x->type == NULL) goto out; if (x->type->init_state(x, NULL)) @@ -1024,6 +1031,8 @@ out: kfree(x->aalg); if (x->ealg) kfree(x->ealg); + if (x->calg) + kfree(x->calg); kfree(x); return ERR_PTR(-ENOBUFS); } @@ -1039,10 +1048,12 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h struct sadb_x_sa2 *sa2; struct sadb_address *saddr, *daddr; struct sadb_msg *out_hdr; - struct xfrm_state *x; + struct xfrm_state *x = NULL; u8 mode; u16 reqid; u8 proto; + unsigned short family; + xfrm_address_t *xsaddr = NULL, *xdaddr = NULL; if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1], ext_hdrs[SADB_EXT_ADDRESS_DST-1])) @@ -1063,23 +1074,21 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1]; daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1]; - switch (((struct sockaddr *)(saddr + 1))->sa_family) { + family = ((struct sockaddr *)(saddr + 1))->sa_family; + switch (family) { case AF_INET: - x = xfrm_find_acq(mode, reqid, proto, - ((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr, - ((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1); + xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr; + xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr; break; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: - x = xfrm6_find_acq(mode, reqid, proto, - &((struct sockaddr_in6 *)(daddr + 1))->sin6_addr, - &((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1); + xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr; + xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr; break; #endif - default: - x = NULL; - break; } + if (xdaddr) + x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family); if (x == NULL) return -ENOENT; @@ -1166,23 +1175,9 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, /* XXX there is race condition */ x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs); if (!x1) { - switch (x->props.family) { - case AF_INET: - x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto, - x->id.daddr.xfrm4_addr, - x->props.saddr.xfrm4_addr, 0); - break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: - x1 = xfrm6_find_acq(x->props.mode, x->props.reqid, x->id.proto, - (struct in6_addr*)x->id.daddr.a6, - (struct in6_addr*)x->props.saddr.a6, 0); - break; -#endif - default: - x1 = NULL; - break; - } + x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto, + &x->id.daddr, + &x->props.saddr, 0, x->props.family); if (x1 && x1->id.spi != x->id.spi && x1->id.spi) { xfrm_state_put(x1); x1 = NULL; @@ -1521,11 +1516,11 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) sin = (void*)(rq+1); if (sin->sin_family != AF_INET) return -EINVAL; - t->saddr.xfrm4_addr = sin->sin_addr.s_addr; + t->saddr.a4 = sin->sin_addr.s_addr; sin++; if (sin->sin_family != AF_INET) return -EINVAL; - t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr; + t->id.daddr.a4 = sin->sin_addr.s_addr; break; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: @@ -1632,7 +1627,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i if (xp->family == AF_INET) { sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr; + sin->sin_addr.s_addr = xp->selector.saddr.a4; sin->sin_port = xp->selector.sport; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -1663,7 +1658,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i if (xp->family == AF_INET) { sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr; + sin->sin_addr.s_addr = xp->selector.daddr.a4; sin->sin_port = xp->selector.dport; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -1751,12 +1746,12 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i case AF_INET: sin = (void*)(rq+1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = t->saddr.xfrm4_addr; + sin->sin_addr.s_addr = t->saddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); sin++; sin->sin_family = AF_INET; - sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr; + sin->sin_addr.s_addr = t->id.daddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); break; @@ -2340,7 +2335,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr; + sin->sin_addr.s_addr = x->props.saddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -2374,7 +2369,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; - sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr; + sin->sin_addr.s_addr = x->id.daddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } @@ -2416,8 +2411,23 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt, struct xfrm_policy *xp; struct sadb_x_policy *pol = (struct sadb_x_policy*)data; - if (opt != IP_IPSEC_POLICY) { - *dir = -EOPNOTSUPP; + switch (family) { + case AF_INET: + if (opt != IP_IPSEC_POLICY) { + *dir = -EOPNOTSUPP; + return NULL; + } + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + if (opt != IPV6_IPSEC_POLICY) { + *dir = -EOPNOTSUPP; + return NULL; + } + break; +#endif + default: + *dir = -EINVAL; return NULL; } diff --git a/net/netsyms.c b/net/netsyms.c index 29bf1aa61b0d..8c508c485c0f 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -292,6 +292,7 @@ extern int (*dlci_ioctl_hook)(unsigned int, void *); EXPORT_SYMBOL(dlci_ioctl_hook); #endif +EXPORT_SYMBOL(xfrm_user_policy); EXPORT_SYMBOL(km_waitq); EXPORT_SYMBOL(xfrm_cfg_sem); EXPORT_SYMBOL(xfrm_policy_alloc); @@ -302,14 +303,20 @@ EXPORT_SYMBOL(__xfrm_policy_check); EXPORT_SYMBOL(__xfrm_route_forward); EXPORT_SYMBOL(xfrm_state_alloc); EXPORT_SYMBOL(__xfrm_state_destroy); -EXPORT_SYMBOL(xfrm4_state_find); +EXPORT_SYMBOL(xfrm_state_find); EXPORT_SYMBOL(xfrm_state_insert); EXPORT_SYMBOL(xfrm_state_check_expire); EXPORT_SYMBOL(xfrm_state_check_space); -EXPORT_SYMBOL(xfrm4_state_lookup); +EXPORT_SYMBOL(xfrm_state_lookup); +EXPORT_SYMBOL(xfrm_state_register_afinfo); +EXPORT_SYMBOL(xfrm_state_unregister_afinfo); +EXPORT_SYMBOL(xfrm_state_get_afinfo); +EXPORT_SYMBOL(xfrm_state_put_afinfo); EXPORT_SYMBOL(xfrm_replay_check); EXPORT_SYMBOL(xfrm_replay_advance); EXPORT_SYMBOL(xfrm_check_selectors); +EXPORT_SYMBOL(__secpath_destroy); +EXPORT_SYMBOL(xfrm_parse_spi); EXPORT_SYMBOL(xfrm4_rcv); EXPORT_SYMBOL(xfrm_register_type); EXPORT_SYMBOL(xfrm_unregister_type); @@ -330,28 +337,24 @@ EXPORT_SYMBOL(xfrm_policy_walk); EXPORT_SYMBOL(xfrm_policy_flush); EXPORT_SYMBOL(xfrm_policy_byid); EXPORT_SYMBOL(xfrm_policy_list); -EXPORT_SYMBOL(xfrm_dst_lookup_register); -EXPORT_SYMBOL(xfrm_dst_lookup_unregister); -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -EXPORT_SYMBOL(xfrm6_state_find); -EXPORT_SYMBOL(xfrm6_rcv); -EXPORT_SYMBOL(xfrm6_state_lookup); -EXPORT_SYMBOL(xfrm6_find_acq); -EXPORT_SYMBOL(xfrm6_register_type); -EXPORT_SYMBOL(xfrm6_unregister_type); -EXPORT_SYMBOL(xfrm6_get_type); -EXPORT_SYMBOL(xfrm6_clear_mutable_options); -#endif +EXPORT_SYMBOL(xfrm_dst_lookup); +EXPORT_SYMBOL(xfrm_policy_register_afinfo); +EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); +EXPORT_SYMBOL(xfrm_policy_get_afinfo); +EXPORT_SYMBOL(xfrm_policy_put_afinfo); EXPORT_SYMBOL_GPL(xfrm_probe_algs); EXPORT_SYMBOL_GPL(xfrm_count_auth_supported); EXPORT_SYMBOL_GPL(xfrm_count_enc_supported); EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx); EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx); +EXPORT_SYMBOL_GPL(xfrm_calg_get_byidx); EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid); EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid); +EXPORT_SYMBOL_GPL(xfrm_calg_get_byid); EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname); EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname); +EXPORT_SYMBOL_GPL(xfrm_calg_get_byname); #if defined(CONFIG_INET_AH) || defined(CONFIG_INET_AH_MODULE) || defined(CONFIG_INET6_AH) || defined(CONFIG_INET6_AH_MODULE) EXPORT_SYMBOL_GPL(skb_ah_walk); #endif diff --git a/net/sctp/input.c b/net/sctp/input.c index 8e67351f419d..5d10625416ef 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -110,6 +110,7 @@ int sctp_rcv(struct sk_buff *skb) struct sctphdr *sh; union sctp_addr src; union sctp_addr dest; + int family; struct sctp_af *af; int ret = 0; @@ -129,7 +130,8 @@ int sctp_rcv(struct sk_buff *skb) skb_pull(skb, sizeof(struct sctphdr)); - af = sctp_get_af_specific(ipver2af(skb->nh.iph->version)); + family = ipver2af(skb->nh.iph->version); + af = sctp_get_af_specific(family); if (unlikely(!af)) goto bad_packet; @@ -173,7 +175,7 @@ int sctp_rcv(struct sk_buff *skb) rcvr = asoc ? &asoc->base : &ep->base; sk = rcvr->sk; - if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) + if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family)) goto discard_release; ret = sk_filter(sk, skb, 1); |
