* [PATH] IPv6 IPsec support
@ 2003-03-05 14:30 Kazunori Miyazawa
2003-03-05 15:21 ` David S. Miller
2003-03-05 23:25 ` [PATH] " David S. Miller
0 siblings, 2 replies; 10+ messages in thread
From: Kazunori Miyazawa @ 2003-03-05 14:30 UTC (permalink / raw)
To: davem, kuznet; +Cc: linux-kernel, netdev, usagi-core
Hello,
I submit the patch to let the kernel support ipv6 ipsec again.
It is able to comple ipv6 as module.
This patch incldes a couple of clean-up and
changes of function name.
Sorry, this patch is for linux-2.5.63.
Best Regards,
--Kazunori Miyazawa (Yokogawa Electric Corporation)
Patch-Name: IPsec
Patch-Id: IPSEC_2_5_63_ALL-20030304
Patch-Author: Kazunori Miyazawa <miyazawa@linux-ipv6.org>
Credit: Kazunori Miyazawa <miyazawa@linux-ipv6.org>,
Mitsuru Kanda <kanda@karaba.org>,
YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>,
Kunihiro Ishiguro
This patch make the kernel process IPv6 packet with IPsec.
- We've introduced a function pointer (xfrm_dst_lookup) for looking up
routing table of each address family to comple ipv6 as module.
- We moved some common functions among protocols such as skb_icv_walk()
in net/ipv4/{ah.c,esp.c} to net/ipv4/xfrm_algo.c. This is for
compling ah / esp and ah6 / esp6 as modules.
- We renamed some IPv4 specific xfrm_XXX() functions to xfrm4_XXX().
diff -ruN -x CVS linux-2.5.63/include/linux/ipv6.h linux25/include/linux/ipv6.h
--- linux-2.5.63/include/linux/ipv6.h 2003-02-25 04:05:38.000000000 +0900
+++ linux25/include/linux/ipv6.h 2003-03-05 11:30:34.000000000 +0900
@@ -74,6 +74,21 @@
#define rt0_type rt_hdr.type;
};
+struct ipv6_auth_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen; /* This one is measured in 32 bit units! */
+ __u16 reserved;
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 auth_data[4]; /* Length variable but >=4. Mind the 64 bit alignment! */
+};
+
+struct ipv6_esp_hdr {
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 enc_data[8]; /* Length variable but >=8. Mind the 64 bit alignment! */
+};
+
/*
* IPv6 fixed header
*
diff -ruN -x CVS linux-2.5.63/include/net/dst.h linux25/include/net/dst.h
--- linux-2.5.63/include/net/dst.h 2003-02-25 04:05:44.000000000 +0900
+++ linux25/include/net/dst.h 2003-03-05 17:49:50.000000000 +0900
@@ -247,7 +247,10 @@
struct flowi;
extern int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
struct sock *sk, int flags);
+extern int xfrm6_lookup(struct dst_entry **dst_p, struct flowi *fl,
+ struct sock *sk, int flags);
extern void xfrm_init(void);
+extern void xfrm6_init(void);
#endif
diff -ruN -x CVS linux-2.5.63/include/net/ip6_route.h linux25/include/net/ip6_route.h
--- linux-2.5.63/include/net/ip6_route.h 2003-02-25 04:05:12.000000000 +0900
+++ linux25/include/net/ip6_route.h 2003-03-04 20:38:14.000000000 +0900
@@ -38,6 +38,7 @@
extern int ipv6_route_ioctl(unsigned int cmd, void *arg);
extern int ip6_route_add(struct in6_rtmsg *rtmsg);
+extern int ip6_route_del(struct in6_rtmsg *rtmsg);
extern int ip6_del_rt(struct rt6_info *);
extern int ip6_rt_addr_add(struct in6_addr *addr,
@@ -57,6 +58,8 @@
struct in6_addr *saddr,
int oif, int flags);
+extern struct rt6_info *ndisc_get_dummy_rt(void);
+
/*
* support functions for ND
*
diff -ruN -x CVS linux-2.5.63/include/net/xfrm.h linux25/include/net/xfrm.h
--- linux-2.5.63/include/net/xfrm.h 2003-02-25 04:05:41.000000000 +0900
+++ linux25/include/net/xfrm.h 2003-03-05 17:49:51.000000000 +0900
@@ -12,6 +12,7 @@
#include <net/dst.h>
#include <net/route.h>
+#include <net/ip6_fib.h>
#define XFRM_ALIGN8(len) (((len) + 7) & ~7)
@@ -282,6 +283,7 @@
struct xfrm_dst *next;
struct dst_entry dst;
struct rtable rt;
+ struct rt6_info rt6;
} u;
};
@@ -308,26 +310,42 @@
if (sp && atomic_dec_and_test(&sp->refcnt))
__secpath_destroy(sp);
}
-
-extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb);
+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)
{
if (sk && sk->policy[XFRM_POLICY_IN])
- return __xfrm_policy_check(sk, dir, skb);
+ return __xfrm_policy_check(sk, dir, skb, AF_INET);
return !xfrm_policy_list[dir] ||
(skb->dst->flags & DST_NOPOLICY) ||
- __xfrm_policy_check(sk, dir, skb);
+ __xfrm_policy_check(sk, dir, skb, AF_INET);
}
-extern int __xfrm_route_forward(struct sk_buff *skb);
+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);
+}
+
+extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
static inline int xfrm_route_forward(struct sk_buff *skb)
{
return !xfrm_policy_list[XFRM_POLICY_OUT] ||
(skb->dst->flags & DST_NOXFRM) ||
- __xfrm_route_forward(skb);
+ __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);
}
extern int __xfrm_sk_clone_policy(struct sock *sk);
@@ -380,12 +398,16 @@
extern void xfrm_input_init(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 *xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err);
+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 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 *xfrm_state_lookup(u32 daddr, u32 spi, u8 proto);
+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_find_acq_byseq(u32 seq);
extern void xfrm_state_delete(struct xfrm_state *x);
extern void xfrm_state_flush(u8 proto);
@@ -393,17 +415,21 @@
extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
+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);
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);
+struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned short family);
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
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);
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);
@@ -425,23 +451,129 @@
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 !memcmp(fl->fl6_dst, sel->daddr.a6, sizeof(struct in6_addr)) &&
- !((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) &&
- !memcmp(fl->fl6_src, sel->saddr.a6, sizeof(struct in6_addr));
+ 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_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto);
-struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create);
-void xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
+struct ah_data
+{
+ u8 *key;
+ int key_len;
+ u8 *work_icv;
+ int icv_full_len;
+ int icv_trunc_len;
+
+ void (*icv)(struct ah_data*,
+ struct sk_buff *skb, u8 *icv);
+
+ struct crypto_tfm *tfm;
+};
+
+struct esp_data
+{
+ /* Confidentiality */
+ struct {
+ u8 *key; /* Key */
+ int key_len; /* Key length */
+ u8 *ivec; /* ivec buffer */
+ /* ivlen is offset from enc_data, where encrypted data start.
+ * It is logically different of crypto_tfm_alg_ivsize(tfm).
+ * We assume that it is either zero (no ivec), or
+ * >= crypto_tfm_alg_ivsize(tfm). */
+ int ivlen;
+ int padlen; /* 0..255 */
+ struct crypto_tfm *tfm; /* crypto handle */
+ } conf;
+
+ /* Integrity. It is active when icv_full_len != 0 */
+ struct {
+ u8 *key; /* Key */
+ int key_len; /* Length of the key */
+ u8 *work_icv;
+ int icv_full_len;
+ int icv_trunc_len;
+ void (*icv)(struct esp_data*,
+ struct sk_buff *skb,
+ int offset, int len, u8 *icv);
+ struct crypto_tfm *tfm;
+ } auth;
+};
+
+typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsigned int);
+extern void skb_ah_walk(const struct sk_buff *skb,
+ struct crypto_tfm *tfm, icv_update_fn_t icv_update);
+extern void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
+ int offset, int len, icv_update_fn_t icv_update);
+extern int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len);
+extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
+extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len);
+
+static inline void
+ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
+{
+ struct crypto_tfm *tfm = ahp->tfm;
+
+ memset(auth_data, 0, ahp->icv_trunc_len);
+ crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
+ skb_ah_walk(skb, tfm, crypto_hmac_update);
+ crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_icv);
+ memcpy(auth_data, ahp->work_icv, ahp->icv_trunc_len);
+}
+
+static inline void
+esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
+ int len, u8 *auth_data)
+{
+ struct crypto_tfm *tfm = esp->auth.tfm;
+ char *icv = esp->auth.work_icv;
+
+ memset(auth_data, 0, esp->auth.icv_trunc_len);
+ crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
+ skb_icv_walk(skb, tfm, offset, len, crypto_hmac_update);
+ crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, icv);
+ memcpy(auth_data, icv, esp->auth.icv_trunc_len);
+}
+
+
+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 -ruN -x CVS linux-2.5.63/net/ipv4/ah.c linux25/net/ipv4/ah.c
--- linux-2.5.63/net/ipv4/ah.c 2003-02-25 04:05:42.000000000 +0900
+++ linux25/net/ipv4/ah.c 2003-03-05 17:49:52.000000000 +0900
@@ -7,25 +7,8 @@
#include <net/icmp.h>
#include <asm/scatterlist.h>
-#define AH_HLEN_NOICV 12
-
-typedef void (icv_update_fn_t)(struct crypto_tfm *,
- struct scatterlist *, unsigned int);
-
-struct ah_data
-{
- u8 *key;
- int key_len;
- u8 *work_icv;
- int icv_full_len;
- int icv_trunc_len;
-
- void (*icv)(struct ah_data*,
- struct sk_buff *skb, u8 *icv);
-
- struct crypto_tfm *tfm;
-};
+#define AH_HLEN_NOICV 12
/* Clear mutable options and find final destination to substitute
* into IP header for icv calculation. Options are already checked
@@ -71,92 +54,6 @@
return 0;
}
-static void skb_ah_walk(const struct sk_buff *skb,
- struct crypto_tfm *tfm, icv_update_fn_t icv_update)
-{
- int offset = 0;
- int len = skb->len;
- int start = skb->len - skb->data_len;
- int i, copy = start - offset;
- struct scatterlist sg;
-
- /* Checksum header. */
- if (copy > 0) {
- if (copy > len)
- copy = len;
-
- sg.page = virt_to_page(skb->data + offset);
- sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
- sg.length = copy;
-
- icv_update(tfm, &sg, 1);
-
- if ((len -= copy) == 0)
- return;
- offset += copy;
- }
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + skb_shinfo(skb)->frags[i].size;
- if ((copy = end - offset) > 0) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- if (copy > len)
- copy = len;
-
- sg.page = frag->page;
- sg.offset = frag->page_offset + offset-start;
- sg.length = copy;
-
- icv_update(tfm, &sg, 1);
-
- if (!(len -= copy))
- return;
- offset += copy;
- }
- start = end;
- }
-
- if (skb_shinfo(skb)->frag_list) {
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
- for (; list; list = list->next) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + list->len;
- if ((copy = end - offset) > 0) {
- if (copy > len)
- copy = len;
- skb_ah_walk(list, tfm, icv_update);
- if ((len -= copy) == 0)
- return;
- offset += copy;
- }
- start = end;
- }
- }
- if (len)
- BUG();
-}
-
-static void
-ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
-{
- struct crypto_tfm *tfm = ahp->tfm;
-
- memset(auth_data, 0, ahp->icv_trunc_len);
- crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
- skb_ah_walk(skb, tfm, crypto_hmac_update);
- crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_icv);
- memcpy(auth_data, ahp->work_icv, ahp->icv_trunc_len);
-}
-
static int ah_output(struct sk_buff *skb)
{
int err;
@@ -330,7 +227,7 @@
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
+ x = xfrm4_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n",
diff -ruN -x CVS linux-2.5.63/net/ipv4/esp.c linux25/net/ipv4/esp.c
--- linux-2.5.63/net/ipv4/esp.c 2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv4/esp.c 2003-03-05 17:49:52.000000000 +0900
@@ -8,312 +8,8 @@
#include <linux/random.h>
#include <net/icmp.h>
-#define MAX_SG_ONSTACK 4
-
-typedef void (icv_update_fn_t)(struct crypto_tfm *,
- struct scatterlist *, unsigned int);
-
-/* BUGS:
- * - we assume replay seqno is always present.
- */
-
-struct esp_data
-{
- /* Confidentiality */
- struct {
- u8 *key; /* Key */
- int key_len; /* Key length */
- u8 *ivec; /* ivec buffer */
- /* ivlen is offset from enc_data, where encrypted data start.
- * It is logically different of crypto_tfm_alg_ivsize(tfm).
- * We assume that it is either zero (no ivec), or
- * >= crypto_tfm_alg_ivsize(tfm). */
- int ivlen;
- int padlen; /* 0..255 */
- struct crypto_tfm *tfm; /* crypto handle */
- } conf;
-
- /* Integrity. It is active when icv_full_len != 0 */
- struct {
- u8 *key; /* Key */
- int key_len; /* Length of the key */
- u8 *work_icv;
- int icv_full_len;
- int icv_trunc_len;
- void (*icv)(struct esp_data*,
- struct sk_buff *skb,
- int offset, int len, u8 *icv);
- struct crypto_tfm *tfm;
- } auth;
-};
-
-/* Move to common area: it is shared with AH. */
-
-void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
- int offset, int len, icv_update_fn_t icv_update)
-{
- int start = skb->len - skb->data_len;
- int i, copy = start - offset;
- struct scatterlist sg;
-
- /* Checksum header. */
- if (copy > 0) {
- if (copy > len)
- copy = len;
-
- sg.page = virt_to_page(skb->data + offset);
- sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
- sg.length = copy;
-
- icv_update(tfm, &sg, 1);
-
- if ((len -= copy) == 0)
- return;
- offset += copy;
- }
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + skb_shinfo(skb)->frags[i].size;
- if ((copy = end - offset) > 0) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- if (copy > len)
- copy = len;
-
- sg.page = frag->page;
- sg.offset = frag->page_offset + offset-start;
- sg.length = copy;
-
- icv_update(tfm, &sg, 1);
-
- if (!(len -= copy))
- return;
- offset += copy;
- }
- start = end;
- }
-
- if (skb_shinfo(skb)->frag_list) {
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
- for (; list; list = list->next) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + list->len;
- if ((copy = end - offset) > 0) {
- if (copy > len)
- copy = len;
- skb_icv_walk(list, tfm, offset-start, copy, icv_update);
- if ((len -= copy) == 0)
- return;
- offset += copy;
- }
- start = end;
- }
- }
- if (len)
- BUG();
-}
-
-
-/* Looking generic it is not used in another places. */
-
-int
-skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
-{
- int start = skb->len - skb->data_len;
- int i, copy = start - offset;
- int elt = 0;
-
- if (copy > 0) {
- if (copy > len)
- copy = len;
- sg[elt].page = virt_to_page(skb->data + offset);
- sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
- sg[elt].length = copy;
- elt++;
- if ((len -= copy) == 0)
- return elt;
- offset += copy;
- }
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + skb_shinfo(skb)->frags[i].size;
- if ((copy = end - offset) > 0) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- if (copy > len)
- copy = len;
- sg[elt].page = frag->page;
- sg[elt].offset = frag->page_offset+offset-start;
- sg[elt].length = copy;
- elt++;
- if (!(len -= copy))
- return elt;
- offset += copy;
- }
- start = end;
- }
-
- if (skb_shinfo(skb)->frag_list) {
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
- for (; list; list = list->next) {
- int end;
-
- BUG_TRAP(start <= offset + len);
-
- end = start + list->len;
- if ((copy = end - offset) > 0) {
- if (copy > len)
- copy = len;
- elt += skb_to_sgvec(list, sg+elt, offset - start, copy);
- if ((len -= copy) == 0)
- return elt;
- offset += copy;
- }
- start = end;
- }
- }
- if (len)
- BUG();
- return elt;
-}
-
-/* Common with AH after some work on arguments. */
-
-static void
-esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
- int len, u8 *auth_data)
-{
- struct crypto_tfm *tfm = esp->auth.tfm;
- char *icv = esp->auth.work_icv;
-
- memset(auth_data, 0, esp->auth.icv_trunc_len);
- crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
- skb_icv_walk(skb, tfm, offset, len, crypto_hmac_update);
- crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, icv);
- memcpy(auth_data, icv, esp->auth.icv_trunc_len);
-}
-
-/* Check that skb data bits are writable. If they are not, copy data
- * to newly created private area. If "tailbits" is given, make sure that
- * tailbits bytes beyond current end of skb are writable.
- *
- * Returns amount of elements of scatterlist to load for subsequent
- * transformations and pointer to writable trailer skb.
- */
-
-int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
-{
- int copyflag;
- int elt;
- struct sk_buff *skb1, **skb_p;
-
- /* If skb is cloned or its head is paged, reallocate
- * head pulling out all the pages (pages are considered not writable
- * at the moment even if they are anonymous).
- */
- if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
- __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
- return -ENOMEM;
-
- /* Easy case. Most of packets will go this way. */
- if (!skb_shinfo(skb)->frag_list) {
- /* A little of trouble, not enough of space for trailer.
- * This should not happen, when stack is tuned to generate
- * good frames. OK, on miss we reallocate and reserve even more
- * space, 128 bytes is fair. */
-
- if (skb_tailroom(skb) < tailbits &&
- pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC))
- return -ENOMEM;
-
- /* Voila! */
- *trailer = skb;
- return 1;
- }
-
- /* Misery. We are in troubles, going to mincer fragments... */
- elt = 1;
- skb_p = &skb_shinfo(skb)->frag_list;
- copyflag = 0;
-
- while ((skb1 = *skb_p) != NULL) {
- int ntail = 0;
-
- /* The fragment is partially pulled by someone,
- * this can happen on input. Copy it and everything
- * after it. */
-
- if (skb_shared(skb1))
- copyflag = 1;
-
- /* If the skb is the last, worry about trailer. */
-
- if (skb1->next == NULL && tailbits) {
- if (skb_shinfo(skb1)->nr_frags ||
- skb_shinfo(skb1)->frag_list ||
- skb_tailroom(skb1) < tailbits)
- ntail = tailbits + 128;
- }
-
- if (copyflag ||
- skb_cloned(skb1) ||
- ntail ||
- skb_shinfo(skb1)->nr_frags ||
- skb_shinfo(skb1)->frag_list) {
- struct sk_buff *skb2;
-
- /* Fuck, we are miserable poor guys... */
- if (ntail == 0)
- skb2 = skb_copy(skb1, GFP_ATOMIC);
- else
- skb2 = skb_copy_expand(skb1,
- skb_headroom(skb1),
- ntail,
- GFP_ATOMIC);
- if (unlikely(skb2 == NULL))
- return -ENOMEM;
-
- if (skb1->sk)
- skb_set_owner_w(skb, skb1->sk);
-
- /* Looking around. Are we still alive?
- * OK, link new skb, drop old one */
-
- skb2->next = skb1->next;
- *skb_p = skb2;
- kfree_skb(skb1);
- skb1 = skb2;
- }
- elt++;
- *trailer = skb1;
- skb_p = &skb1->next;
- }
-
- return elt;
-}
-
-void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
-{
- if (tail != skb) {
- skb->data_len += len;
- skb->len += len;
- }
- return skb_put(tail, len);
-}
+#define MAX_SG_ONSTACK 4
int esp_output(struct sk_buff *skb)
{
@@ -575,7 +271,7 @@
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
+ x = xfrm4_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n",
diff -ruN -x CVS linux-2.5.63/net/ipv4/route.c linux25/net/ipv4/route.c
--- linux-2.5.63/net/ipv4/route.c 2003-02-25 04:06:01.000000000 +0900
+++ linux25/net/ipv4/route.c 2003-03-04 20:38:15.000000000 +0900
@@ -96,6 +96,7 @@
#include <net/arp.h>
#include <net/tcp.h>
#include <net/icmp.h>
+#include <net/xfrm.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
@@ -2599,6 +2600,13 @@
#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;
@@ -2680,6 +2688,7 @@
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;
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_algo.c linux25/net/ipv4/xfrm_algo.c
--- linux-2.5.63/net/ipv4/xfrm_algo.c 2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/ipv4/xfrm_algo.c 2003-03-04 20:38:16.000000000 +0900
@@ -8,9 +8,11 @@
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pfkeyv2.h>
#include <net/xfrm.h>
+#include <asm/scatterlist.h>
/*
* Algorithms supported by IPsec. These entries contain properties which
@@ -348,3 +350,333 @@
n++;
return n;
}
+
+#if defined(CONFIG_INET_AH) || defined(CONFIG_INET_AH_MODULE) || defined(CONFIG_INET6_AH) || defined(CONFIG_INET6_AH_MODULE)
+void skb_ah_walk(const struct sk_buff *skb,
+ struct crypto_tfm *tfm, icv_update_fn_t icv_update)
+{
+ int offset = 0;
+ int len = skb->len;
+ int start = skb->len - skb->data_len;
+ int i, copy = start - offset;
+ struct scatterlist sg;
+
+ /* Checksum header. */
+ if (copy > 0) {
+ if (copy > len)
+ copy = len;
+
+ sg.page = virt_to_page(skb->data + offset);
+ sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+ sg.length = copy;
+
+ icv_update(tfm, &sg, 1);
+
+ if ((len -= copy) == 0)
+ return;
+ offset += copy;
+ }
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + skb_shinfo(skb)->frags[i].size;
+ if ((copy = end - offset) > 0) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ if (copy > len)
+ copy = len;
+
+ sg.page = frag->page;
+ sg.offset = frag->page_offset + offset-start;
+ sg.length = copy;
+
+ icv_update(tfm, &sg, 1);
+
+ if (!(len -= copy))
+ return;
+ offset += copy;
+ }
+ start = end;
+ }
+
+ if (skb_shinfo(skb)->frag_list) {
+ struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+ for (; list; list = list->next) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + list->len;
+ if ((copy = end - offset) > 0) {
+ if (copy > len)
+ copy = len;
+ skb_ah_walk(list, tfm, icv_update);
+ if ((len -= copy) == 0)
+ return;
+ offset += copy;
+ }
+ start = end;
+ }
+ }
+ if (len)
+ BUG();
+}
+#endif
+
+#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
+/* Move to common area: it is shared with AH. */
+
+void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
+ int offset, int len, icv_update_fn_t icv_update)
+{
+ int start = skb->len - skb->data_len;
+ int i, copy = start - offset;
+ struct scatterlist sg;
+
+ /* Checksum header. */
+ if (copy > 0) {
+ if (copy > len)
+ copy = len;
+
+ sg.page = virt_to_page(skb->data + offset);
+ sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+ sg.length = copy;
+
+ icv_update(tfm, &sg, 1);
+
+ if ((len -= copy) == 0)
+ return;
+ offset += copy;
+ }
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + skb_shinfo(skb)->frags[i].size;
+ if ((copy = end - offset) > 0) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ if (copy > len)
+ copy = len;
+
+ sg.page = frag->page;
+ sg.offset = frag->page_offset + offset-start;
+ sg.length = copy;
+
+ icv_update(tfm, &sg, 1);
+
+ if (!(len -= copy))
+ return;
+ offset += copy;
+ }
+ start = end;
+ }
+
+ if (skb_shinfo(skb)->frag_list) {
+ struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+ for (; list; list = list->next) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + list->len;
+ if ((copy = end - offset) > 0) {
+ if (copy > len)
+ copy = len;
+ skb_icv_walk(list, tfm, offset-start, copy, icv_update);
+ if ((len -= copy) == 0)
+ return;
+ offset += copy;
+ }
+ start = end;
+ }
+ }
+ if (len)
+ BUG();
+}
+
+
+/* Looking generic it is not used in another places. */
+
+int
+skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+ int start = skb->len - skb->data_len;
+ int i, copy = start - offset;
+ int elt = 0;
+
+ if (copy > 0) {
+ if (copy > len)
+ copy = len;
+ sg[elt].page = virt_to_page(skb->data + offset);
+ sg[elt].offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+ sg[elt].length = copy;
+ elt++;
+ if ((len -= copy) == 0)
+ return elt;
+ offset += copy;
+ }
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + skb_shinfo(skb)->frags[i].size;
+ if ((copy = end - offset) > 0) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ if (copy > len)
+ copy = len;
+ sg[elt].page = frag->page;
+ sg[elt].offset = frag->page_offset+offset-start;
+ sg[elt].length = copy;
+ elt++;
+ if (!(len -= copy))
+ return elt;
+ offset += copy;
+ }
+ start = end;
+ }
+
+ if (skb_shinfo(skb)->frag_list) {
+ struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+ for (; list; list = list->next) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + list->len;
+ if ((copy = end - offset) > 0) {
+ if (copy > len)
+ copy = len;
+ elt += skb_to_sgvec(list, sg+elt, offset - start, copy);
+ if ((len -= copy) == 0)
+ return elt;
+ offset += copy;
+ }
+ start = end;
+ }
+ }
+ if (len)
+ BUG();
+ return elt;
+}
+
+/* Check that skb data bits are writable. If they are not, copy data
+ * to newly created private area. If "tailbits" is given, make sure that
+ * tailbits bytes beyond current end of skb are writable.
+ *
+ * Returns amount of elements of scatterlist to load for subsequent
+ * transformations and pointer to writable trailer skb.
+ */
+
+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
+{
+ int copyflag;
+ int elt;
+ struct sk_buff *skb1, **skb_p;
+
+ /* If skb is cloned or its head is paged, reallocate
+ * head pulling out all the pages (pages are considered not writable
+ * at the moment even if they are anonymous).
+ */
+ if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
+ __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
+ return -ENOMEM;
+
+ /* Easy case. Most of packets will go this way. */
+ if (!skb_shinfo(skb)->frag_list) {
+ /* A little of trouble, not enough of space for trailer.
+ * This should not happen, when stack is tuned to generate
+ * good frames. OK, on miss we reallocate and reserve even more
+ * space, 128 bytes is fair. */
+
+ if (skb_tailroom(skb) < tailbits &&
+ pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC))
+ return -ENOMEM;
+
+ /* Voila! */
+ *trailer = skb;
+ return 1;
+ }
+
+ /* Misery. We are in troubles, going to mincer fragments... */
+
+ elt = 1;
+ skb_p = &skb_shinfo(skb)->frag_list;
+ copyflag = 0;
+
+ while ((skb1 = *skb_p) != NULL) {
+ int ntail = 0;
+
+ /* The fragment is partially pulled by someone,
+ * this can happen on input. Copy it and everything
+ * after it. */
+
+ if (skb_shared(skb1))
+ copyflag = 1;
+
+ /* If the skb is the last, worry about trailer. */
+
+ if (skb1->next == NULL && tailbits) {
+ if (skb_shinfo(skb1)->nr_frags ||
+ skb_shinfo(skb1)->frag_list ||
+ skb_tailroom(skb1) < tailbits)
+ ntail = tailbits + 128;
+ }
+
+ if (copyflag ||
+ skb_cloned(skb1) ||
+ ntail ||
+ skb_shinfo(skb1)->nr_frags ||
+ skb_shinfo(skb1)->frag_list) {
+ struct sk_buff *skb2;
+
+ /* Fuck, we are miserable poor guys... */
+ if (ntail == 0)
+ skb2 = skb_copy(skb1, GFP_ATOMIC);
+ else
+ skb2 = skb_copy_expand(skb1,
+ skb_headroom(skb1),
+ ntail,
+ GFP_ATOMIC);
+ if (unlikely(skb2 == NULL))
+ return -ENOMEM;
+
+ if (skb1->sk)
+ skb_set_owner_w(skb, skb1->sk);
+
+ /* Looking around. Are we still alive?
+ * OK, link new skb, drop old one */
+
+ skb2->next = skb1->next;
+ *skb_p = skb2;
+ kfree_skb(skb1);
+ skb1 = skb2;
+ }
+ elt++;
+ *trailer = skb1;
+ skb_p = &skb1->next;
+ }
+
+ return elt;
+}
+
+void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
+{
+ if (tail != skb) {
+ skb->data_len += len;
+ skb->len += len;
+ }
+ return skb_put(tail, len);
+}
+#endif
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_input.c linux25/net/ipv4/xfrm_input.c
--- linux-2.5.63/net/ipv4/xfrm_input.c 2003-02-25 04:05:05.000000000 +0900
+++ linux25/net/ipv4/xfrm_input.c 2003-03-05 17:49:52.000000000 +0900
@@ -1,4 +1,14 @@
+/* Changes
+ *
+ * Mitsuru KANDA @USAGI : IPv6 Support
+ * Kazunori MIYAZAWA @USAGI :
+ * YOSHIFUJI Hideaki @USAGI :
+ * Kunihiro Ishiguro :
+ *
+ */
+
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
@@ -64,7 +74,7 @@
if (xfrm_nr == XFRM_MAX_DEPTH)
goto drop;
- x = xfrm_state_lookup(iph->daddr, spi, iph->protocol);
+ x = xfrm4_state_lookup(iph->daddr, spi, iph->protocol);
if (x == NULL)
goto drop;
@@ -157,3 +167,288 @@
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);
+ *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;
+}
+
+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 -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_policy.c linux25/net/ipv4/xfrm_policy.c
--- linux-2.5.63/net/ipv4/xfrm_policy.c 2003-02-25 04:05:32.000000000 +0900
+++ linux25/net/ipv4/xfrm_policy.c 2003-03-05 17:49:52.000000000 +0900
@@ -1,6 +1,16 @@
+/* Changes
+ *
+ * Mitsuru KANDA @USAGI : IPv6 Support
+ * Kazunori MIYAZAWA @USAGI :
+ * Kunihiro Ishiguro :
+ *
+ */
+
#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);
@@ -10,6 +20,11 @@
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 inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family);
/* Limited flow cache. Its function now is to accelerate search for
* policy rules.
@@ -48,6 +63,24 @@
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;
@@ -77,13 +110,27 @@
}
}
-struct xfrm_policy *flow_lookup(int dir, struct flowi *fl)
+struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
+ unsigned short family)
{
- struct xfrm_policy *pol;
+ struct xfrm_policy *pol = NULL;
struct flow_entry *fle;
- u32 hash = flow_hash(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;
+ }
+
local_bh_disable();
cpu = smp_processor_id();
@@ -101,7 +148,7 @@
}
}
- pol = xfrm_policy_lookup(dir, fl);
+ pol = xfrm_policy_lookup(dir, fl, family);
if (fle) {
/* Stale flow entry found. Update it. */
@@ -199,6 +246,46 @@
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 err = 0;
+
+ write_lock(&xdl_lock);
+ if (__xfrm_dst_lookup[family])
+ err = -ENOBUFS;
+ else {
+ __xfrm_dst_lookup[family] = dst_lookup;
+ }
+ write_unlock(&xdl_lock);
+
+ 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);
+ else
+ err = -EINVAL;
+ read_unlock(&xdl_lock);
+ 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;
@@ -506,15 +593,32 @@
/* Find policy to apply to this flow. */
-struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl)
+struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl,
+ unsigned short family)
{
struct xfrm_policy *pol;
read_lock_bh(&xfrm_policy_lock);
for (pol = xfrm_policy_list[dir]; pol; pol = pol->next) {
struct xfrm_selector *sel = &pol->selector;
+ int match;
+
+ if (pol->family != family)
+ continue;
- if (xfrm4_selector_match(sel, fl)) {
+ 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;
+ }
+ if (match) {
atomic_inc(&pol->refcnt);
break;
}
@@ -529,7 +633,21 @@
read_lock_bh(&xfrm_policy_lock);
if ((pol = sk->policy[dir]) != NULL) {
- if (xfrm4_selector_match(&pol->selector, fl))
+ 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;
+ }
+ if (match)
atomic_inc(&pol->refcnt);
else
pol = NULL;
@@ -630,8 +748,8 @@
/* Resolve list of templates for the flow, given policy. */
static int
-xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
- struct xfrm_state **xfrm)
+xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
+ struct xfrm_state **xfrm)
{
int nx;
int i, error;
@@ -649,7 +767,53 @@
local = tmpl->saddr.xfrm4_addr;
}
- x = xfrm_state_find(remote, local, fl, tmpl, policy, &error);
+ 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;
+ struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
+
+ if (tmpl->mode) {
+ remote = (struct in6_addr*)&tmpl->id.daddr;
+ local = (struct in6_addr*)&tmpl->saddr;
+ }
+
+ x = xfrm6_state_find(remote, local, fl, tmpl, policy, &error);
if (x && x->km.state == XFRM_STATE_VALID) {
xfrm[nx++] = x;
@@ -673,6 +837,7 @@
xfrm_state_put(xfrm[nx]);
return error;
}
+#endif
/* Check that the bundle accepts the flow and its components are
* still valid.
@@ -694,6 +859,24 @@
return 0;
}
+#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.
@@ -744,7 +927,7 @@
.saddr = local }
}
};
- err = __ip_route_output_key(&rt, &fl_tunnel);
+ err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET);
if (err)
goto error;
} else {
@@ -791,6 +974,97 @@
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;
+
+ 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;
+ }
+
+ 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;
+ 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;
+ }
+ *dst_p = dst;
+ return 0;
+
+error:
+ if (dst)
+ dst_free(dst);
+ return err;
+}
+#endif
+
/* Main function: finds/creates a bundle for given flow.
*
* At the moment we eat a raw IP route. Mostly to speed up lookups
@@ -806,9 +1080,7 @@
int nx = 0;
int err;
u32 genid;
-
- fl->oif = rt->u.dst.dev->ifindex;
- fl->fl4_src = rt->rt_src;
+ u16 family = (*dst_p)->ops->family;
restart:
genid = xfrm_policy_genid;
@@ -821,11 +1093,12 @@
if ((rt->u.dst.flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0;
- policy = flow_lookup(XFRM_POLICY_OUT, fl);
- if (!policy)
- return 0;
+ policy = flow_lookup(XFRM_POLICY_OUT, fl, family);
}
+ if (!policy)
+ return 0;
+
policy->curlft.use_time = (unsigned long)xtime.tv_sec;
switch (policy->action) {
@@ -846,23 +1119,48 @@
* LATER: help from flow cache. It is optional, this
* is required only for output policy.
*/
- 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);
+ if (family == AF_INET) {
+ fl->oif = rt->u.dst.dev->ifindex;
+ fl->fl4_src = rt->rt_src;
+ 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;
}
- read_unlock_bh(&policy->lock);
if (dst)
break;
- nx = xfrm_tmpl_resolve(policy, fl, xfrm);
if (unlikely(nx<0)) {
err = nx;
if (err == -EAGAIN) {
@@ -873,7 +1171,18 @@
__set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue(&km_waitq, &wait);
- err = xfrm_tmpl_resolve(policy, fl, xfrm);
+ 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;
+ }
if (err == -EAGAIN)
schedule();
__set_task_state(tsk, TASK_RUNNING);
@@ -896,7 +1205,19 @@
}
dst = &rt->u.dst;
- err = xfrm_bundle_create(policy, xfrm, nx, fl, &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;
+ }
+
if (unlikely(err)) {
int i;
for (i=0; i<nx; i++)
@@ -962,7 +1283,7 @@
}
static void
-_decode_session(struct sk_buff *skb, struct flowi *fl)
+_decode_session4(struct sk_buff *skb, struct flowi *fl)
{
struct iphdr *iph = skb->nh.iph;
u8 *xprth = skb->nh.raw + iph->ihl*4;
@@ -1008,18 +1329,109 @@
fl->fl4_src = iph->saddr;
}
-int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+static inline int
+xfrm6_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 || !ipv6_addr_any((struct in6_addr*)&x->props.saddr) ||
+ !ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
+}
+
+static inline int
+xfrm6_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
+{
+ for (; idx < sp->len; idx++) {
+ if (xfrm6_state_ok(tmpl, sp->xvec[idx]))
+ return ++idx;
+ }
+ return -1;
+}
+
+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;
+ };
+ }
+}
+#endif
+
+int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
+ unsigned short family)
{
struct xfrm_policy *pol;
struct flowi fl;
- _decode_session(skb, &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 :
+ return 0;
+ }
/* First, check used SA against their selectors. */
if (skb->sp) {
int i;
+
for (i=skb->sp->len-1; i>=0; i--) {
- if (!xfrm4_selector_match(&skb->sp->xvec[i]->sel, &fl))
+ 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)
return 0;
}
}
@@ -1029,7 +1441,7 @@
pol = xfrm_sk_policy_lookup(sk, dir, &fl);
if (!pol)
- pol = flow_lookup(dir, &fl);
+ pol = flow_lookup(dir, &fl, family);
if (!pol)
return 1;
@@ -1050,7 +1462,18 @@
* are implied between each two transformations.
*/
for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
- k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k);
+ 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;
+ }
if (k < 0)
goto reject;
}
@@ -1064,18 +1487,29 @@
return 0;
}
-int __xfrm_route_forward(struct sk_buff *skb)
+int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
struct flowi fl;
- _decode_session(skb, &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:
+ return 0;
+ }
return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
}
/* Optimize later using cookies and generation ids. */
-static struct dst_entry *xfrm4_dst_check(struct dst_entry *dst, u32 cookie)
+static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
{
struct dst_entry *child = dst;
@@ -1091,19 +1525,19 @@
return dst;
}
-static void xfrm4_dst_destroy(struct dst_entry *dst)
+static void xfrm_dst_destroy(struct dst_entry *dst)
{
xfrm_state_put(dst->xfrm);
dst->xfrm = NULL;
}
-static void xfrm4_link_failure(struct sk_buff *skb)
+static void xfrm_link_failure(struct sk_buff *skb)
{
/* Impossible. Such dst must be popped before reaches point of failure. */
return;
}
-static struct dst_entry *xfrm4_negative_advice(struct dst_entry *dst)
+static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
{
if (dst) {
if (dst->obsolete) {
@@ -1114,8 +1548,7 @@
return dst;
}
-
-static int xfrm4_garbage_collect(void)
+static void __xfrm_garbage_collect(void)
{
int i;
struct xfrm_policy *pol;
@@ -1145,10 +1578,22 @@
gc_list = dst->next;
dst_free(dst);
}
+}
+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 {
@@ -1192,7 +1637,7 @@
return 0;
}
-
+
static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
{
struct dst_entry *path = dst->path;
@@ -1203,6 +1648,18 @@
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... :-)
@@ -1212,7 +1669,7 @@
*
* Consider this function as something like dark humour. :-)
*/
-static int xfrm4_get_mss(struct dst_entry *dst, u32 mtu)
+static int xfrm_get_mss(struct dst_entry *dst, u32 mtu)
{
int res = mtu - dst->header_len;
@@ -1247,16 +1704,32 @@
.family = AF_INET,
.protocol = __constant_htons(ETH_P_IP),
.gc = xfrm4_garbage_collect,
- .check = xfrm4_dst_check,
- .destroy = xfrm4_dst_destroy,
- .negative_advice = xfrm4_negative_advice,
- .link_failure = xfrm4_link_failure,
+ .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 = xfrm4_get_mss,
+ .get_mss = xfrm_get_mss,
.gc_thresh = 1024,
.entry_size = sizeof(struct xfrm_dst),
};
+#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 */
+
void __init xfrm_init(void)
{
xfrm4_dst_ops.kmem_cachep = kmem_cache_create("xfrm4_dst_cache",
@@ -1267,8 +1740,12 @@
if (!xfrm4_dst_ops.kmem_cachep)
panic("IP: failed to allocate xfrm4_dst_cache\n");
- flow_cache_init();
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep;
+#endif
+ flow_cache_init();
xfrm_state_init();
xfrm_input_init();
}
+
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_state.c linux25/net/ipv4/xfrm_state.c
--- linux-2.5.63/net/ipv4/xfrm_state.c 2003-02-25 04:05:38.000000000 +0900
+++ linux25/net/ipv4/xfrm_state.c 2003-03-05 20:17:33.000000000 +0900
@@ -1,3 +1,11 @@
+/* Changes
+ *
+ * Mitsuru KANDA @USAGI : IPv6 Support
+ * Kazunori MIYAZAWA @USAGI :
+ * Kunihiro Ishiguro :
+ *
+ */
+
#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
@@ -207,8 +215,8 @@
}
struct xfrm_state *
-xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err)
+xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
+ struct xfrm_policy *pol, int *err)
{
unsigned h = ntohl(daddr);
struct xfrm_state *x;
@@ -290,6 +298,7 @@
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;
@@ -318,14 +327,133 @@
return x;
}
+#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)
+{
+ unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
+ 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) &&
+ x->props.reqid == tmpl->reqid &&
+ (!ipv6_addr_cmp(saddr, (struct in6_addr *)&x->props.saddr)|| ipv6_addr_any(saddr)) &&
+ 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 (!xfrm6_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 (xfrm6_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. */
+ 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;
+
+ 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;
+ 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;
+}
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
void xfrm_state_insert(struct xfrm_state *x)
{
unsigned h = 0;
- if (x->props.family == AF_INET)
+ switch (x->props.family) {
+ case AF_INET:
h = ntohl(x->id.daddr.xfrm4_addr);
- else if (x->props.family == AF_INET6)
+ 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;
@@ -384,7 +512,7 @@
}
struct xfrm_state *
-xfrm_state_lookup(u32 daddr, u32 spi, u8 proto)
+xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto)
{
unsigned h = ntohl(daddr^spi^proto);
struct xfrm_state *x;
@@ -406,6 +534,31 @@
return NULL;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct xfrm_state *
+xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
+{
+ 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)
{
@@ -445,7 +598,59 @@
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;
+
+ 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;
@@ -458,6 +663,7 @@
spin_unlock_bh(&xfrm_state_lock);
return x0;
}
+#endif
/* Silly enough, but I'm lazy to build resolution list */
@@ -491,7 +697,18 @@
return;
if (minspi == maxspi) {
- x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
+ 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;
+ }
if (x0) {
xfrm_state_put(x0);
return;
@@ -503,7 +720,18 @@
maxspi = ntohl(maxspi);
for (h=0; h<maxspi-minspi+1; h++) {
spi = minspi + net_random()%(maxspi-minspi+1);
- x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, htonl(spi), x->id.proto);
+ 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;
+ }
if (x0 == NULL)
break;
xfrm_state_put(x0);
@@ -512,7 +740,18 @@
}
if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock);
- h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
+ 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;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
@@ -605,14 +844,21 @@
int i;
for (i=0; i<n; i++) {
- if (x[i]->props.family == AF_INET &&
- !xfrm4_selector_match(&x[i]->sel, fl))
- return -EINVAL;
+ 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)
- if (x[i]->props.family == AF_INET6 &&
- !xfrm6_selector_match(&x[i]->sel, fl))
- return -EINVAL;
+ case AF_INET6:
+ match = xfrm6_selector_match(&x[i]->sel, fl);
+ break;
#endif
+ default:
+ match = 0;
+ }
+ if (!match)
+ return -EINVAL;
}
return 0;
}
@@ -722,118 +968,3 @@
}
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-struct xfrm_state *
-xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
-{
- 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;
-}
-
-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;
-
- spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (x->props.family == AF_INET6 &&
- !memcmp(daddr, x->id.daddr.a6, sizeof(struct in6_addr)) &&
- mode == x->props.mode &&
- proto == x->id.proto &&
- !memcmp(saddr, x->props.saddr.a6, sizeof(struct in6_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) {
- 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);
- }
- spin_unlock_bh(&xfrm_state_lock);
- return x0;
-}
-
-void
-xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
-{
- u32 h;
- struct xfrm_state *x0;
-
- if (x->id.spi)
- return;
-
- if (minspi == maxspi) {
- x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
- if (x0) {
- xfrm_state_put(x0);
- return;
- }
- x->id.spi = minspi;
- } else {
- u32 spi = 0;
- minspi = ntohl(minspi);
- maxspi = ntohl(maxspi);
- for (h=0; h<maxspi-minspi+1; h++) {
- spi = minspi + net_random()%(maxspi-minspi+1);
- x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, htonl(spi), x->id.proto);
- if (x0 == NULL)
- break;
- xfrm_state_put(x0);
- }
- x->id.spi = htonl(spi);
- }
- if (x->id.spi) {
- spin_lock_bh(&xfrm_state_lock);
- 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;
- list_add(&x->byspi, xfrm_state_byspi+h);
- atomic_inc(&x->refcnt);
- spin_unlock_bh(&xfrm_state_lock);
- wake_up(&km_waitq);
- }
-}
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_user.c linux25/net/ipv4/xfrm_user.c
--- linux-2.5.63/net/ipv4/xfrm_user.c 2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv4/xfrm_user.c 2003-03-04 20:38:16.000000000 +0900
@@ -234,8 +234,8 @@
switch (x->props.family) {
case AF_INET:
- x1 = xfrm_state_lookup(x->props.saddr.xfrm4_addr,
- x->id.spi, x->id.proto);
+ 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:
@@ -265,7 +265,7 @@
switch (p->family) {
case AF_INET:
- x = xfrm_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
+ x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
@@ -395,7 +395,7 @@
switch (p->family) {
case AF_INET:
- x = xfrm_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
+ x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
diff -ruN -x CVS linux-2.5.63/net/ipv6/Kconfig linux25/net/ipv6/Kconfig
--- linux-2.5.63/net/ipv6/Kconfig 2003-02-25 04:05:32.000000000 +0900
+++ linux25/net/ipv6/Kconfig 2003-03-04 20:38:16.000000000 +0900
@@ -17,5 +17,18 @@
See <file:Documentation/networking/ip-sysctl.txt> for details.
-source "net/ipv6/netfilter/Kconfig"
+config INET6_AH
+ tristate "IPv6: AH transformation"
+ ---help---
+ Support for IPsec AH.
+
+ If unsure, say Y.
+
+config INET6_ESP
+ tristate "IPv6: ESP transformation"
+ ---help---
+ Support for IPsec ESP.
+ If unsure, say Y.
+
+source "net/ipv6/netfilter/Kconfig"
diff -ruN -x CVS linux-2.5.63/net/ipv6/Makefile linux25/net/ipv6/Makefile
--- linux-2.5.63/net/ipv6/Makefile 2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/Makefile 2003-03-05 00:28:59.000000000 +0900
@@ -10,4 +10,6 @@
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
ip6_flowlabel.o ipv6_syms.o
+obj-$(CONFIG_INET6_AH) += ah6.o
+obj-$(CONFIG_INET6_ESP) += esp6.o
obj-$(CONFIG_NETFILTER) += netfilter/
diff -ruN -x CVS linux-2.5.63/net/ipv6/ah6.c linux25/net/ipv6/ah6.c
--- linux-2.5.63/net/ipv6/ah6.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25/net/ipv6/ah6.c 2003-03-05 11:32:51.000000000 +0900
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C)2002 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors
+ *
+ * Mitsuru KANDA @USAGI : IPv6 Support
+ * Kazunori MIYAZAWA @USAGI :
+ * Kunihiro Ishiguro :
+ *
+ * This file is derived from net/ipv4/ah.c.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <net/icmp.h>
+#include <net/ipv6.h>
+#include <net/xfrm.h>
+#include <asm/scatterlist.h>
+
+#define AH_HLEN_NOICV 12
+
+/* XXX no ipv6 ah specific */
+#define NIP6(addr) \
+ ntohs((addr).s6_addr16[0]),\
+ ntohs((addr).s6_addr16[1]),\
+ ntohs((addr).s6_addr16[2]),\
+ ntohs((addr).s6_addr16[3]),\
+ ntohs((addr).s6_addr16[4]),\
+ ntohs((addr).s6_addr16[5]),\
+ ntohs((addr).s6_addr16[6]),\
+ ntohs((addr).s6_addr16[7])
+
+int ah6_output(struct sk_buff *skb)
+{
+ int err;
+ int hdr_len = sizeof(struct ipv6hdr);
+ struct dst_entry *dst = skb->dst;
+ struct xfrm_state *x = dst->xfrm;
+ struct ipv6hdr *iph = NULL;
+ struct ip_auth_hdr *ah;
+ struct ah_data *ahp;
+ u16 nh_offset = 0;
+ u8 nexthdr;
+printk(KERN_DEBUG "%s\n", __FUNCTION__);
+ if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL)
+ return -EINVAL;
+
+ spin_lock_bh(&x->lock);
+ if ((err = xfrm_state_check_expire(x)) != 0)
+ goto error;
+ if ((err = xfrm_state_check_space(x, skb)) != 0)
+ goto error;
+
+ if (x->props.mode) {
+ iph = skb->nh.ipv6h;
+ skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+ skb->nh.ipv6h->version = 6;
+ skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+ skb->nh.ipv6h->nexthdr = IPPROTO_AH;
+ memcpy(&skb->nh.ipv6h->saddr, &x->props.saddr, sizeof(struct in6_addr));
+ memcpy(&skb->nh.ipv6h->daddr, &x->id.daddr, sizeof(struct in6_addr));
+ ah = (struct ip_auth_hdr*)(skb->nh.ipv6h+1);
+ ah->nexthdr = IPPROTO_IPV6;
+ } else {
+ hdr_len = skb->h.raw - skb->nh.raw;
+ iph = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!iph) {
+ err = -ENOMEM;
+ goto error;
+ }
+ memcpy(iph, skb->data, hdr_len);
+ skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+ memcpy(skb->nh.ipv6h, iph, hdr_len);
+ nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_OUT);
+ if (nexthdr == 0)
+ goto error;
+
+ skb->nh.raw[nh_offset] = IPPROTO_AH;
+ skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+ ah = (struct ip_auth_hdr*)(skb->nh.raw+hdr_len);
+ skb->h.raw = (unsigned char*) ah;
+ ah->nexthdr = nexthdr;
+ }
+
+ skb->nh.ipv6h->priority = 0;
+ skb->nh.ipv6h->flow_lbl[0] = 0;
+ skb->nh.ipv6h->flow_lbl[1] = 0;
+ skb->nh.ipv6h->flow_lbl[2] = 0;
+ skb->nh.ipv6h->hop_limit = 0;
+
+ ahp = x->data;
+ ah->hdrlen = (XFRM_ALIGN8(ahp->icv_trunc_len +
+ AH_HLEN_NOICV) >> 2) - 2;
+
+ ah->reserved = 0;
+ ah->spi = x->id.spi;
+ ah->seq_no = htonl(++x->replay.oseq);
+ ahp->icv(ahp, skb, ah->auth_data);
+
+ if (x->props.mode) {
+ skb->nh.ipv6h->hop_limit = iph->hop_limit;
+ skb->nh.ipv6h->priority = iph->priority;
+ skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
+ skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
+ skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
+ } else {
+ memcpy(skb->nh.ipv6h, iph, hdr_len);
+ skb->nh.raw[nh_offset] = IPPROTO_AH;
+ skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+ kfree (iph);
+ }
+
+ skb->nh.raw = skb->data;
+
+ x->curlft.bytes += skb->len;
+ x->curlft.packets++;
+ spin_unlock_bh(&x->lock);
+ if ((skb->dst = dst_pop(dst)) == NULL)
+ goto error_nolock;
+ return NET_XMIT_BYPASS;
+error:
+ spin_unlock_bh(&x->lock);
+error_nolock:
+ kfree_skb(skb);
+ return err;
+}
+
+int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+ int ah_hlen;
+ struct ipv6hdr *iph;
+ struct ipv6_auth_hdr *ah;
+ struct ah_data *ahp;
+ unsigned char *tmp_hdr = NULL;
+ int hdr_len = skb->h.raw - skb->nh.raw;
+ u8 nexthdr = 0;
+
+ if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
+ goto out;
+
+ ah = (struct ipv6_auth_hdr*)skb->data;
+ ahp = x->data;
+ ah_hlen = (ah->hdrlen + 2) << 2;
+
+ if (ah_hlen != XFRM_ALIGN8(ahp->icv_full_len + AH_HLEN_NOICV) &&
+ ah_hlen != XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV))
+ goto out;
+
+ if (!pskb_may_pull(skb, ah_hlen))
+ goto out;
+
+ /* We are going to _remove_ AH header to keep sockets happy,
+ * so... Later this can change. */
+ if (skb_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto out;
+
+ tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!tmp_hdr)
+ goto out;
+ memcpy(tmp_hdr, skb->nh.raw, hdr_len);
+ ah = (struct ipv6_auth_hdr*)skb->data;
+ iph = skb->nh.ipv6h;
+
+ {
+ u8 auth_data[ahp->icv_trunc_len];
+
+ memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
+ skb_push(skb, skb->data - skb->nh.raw);
+ ahp->icv(ahp, skb, ah->auth_data);
+ if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ipsec ah authentication error\n");
+ x->stats.integrity_failed++;
+ goto free_out;
+ }
+ }
+
+ nexthdr = ah->nexthdr;
+ skb->nh.raw = skb_pull(skb, (ah->hdrlen+2)<<2);
+ memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+ skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+ skb_pull(skb, hdr_len);
+ skb->h.raw = skb->data;
+
+
+ kfree(tmp_hdr);
+
+ return nexthdr;
+
+free_out:
+ kfree(tmp_hdr);
+out:
+ return -EINVAL;
+}
+
+void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
+ struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
+ struct xfrm_state *x;
+
+ if (type != ICMPV6_DEST_UNREACH ||
+ type != ICMPV6_PKT_TOOBIG)
+ return;
+
+ x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH);
+ if (!x)
+ return;
+
+ printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/"
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ ntohl(ah->spi), NIP6(iph->daddr));
+
+ xfrm_state_put(x);
+}
+
+static int ah6_init_state(struct xfrm_state *x, void *args)
+{
+ struct ah_data *ahp = NULL;
+ struct xfrm_algo_desc *aalg_desc;
+
+ /* null auth can use a zero length key */
+ if (x->aalg->alg_key_len > 512)
+ goto error;
+
+ ahp = kmalloc(sizeof(*ahp), GFP_KERNEL);
+ if (ahp == NULL)
+ return -ENOMEM;
+
+ memset(ahp, 0, sizeof(*ahp));
+
+ ahp->key = x->aalg->alg_key;
+ ahp->key_len = (x->aalg->alg_key_len+7)/8;
+ ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
+ if (!ahp->tfm)
+ goto error;
+ ahp->icv = ah_hmac_digest;
+
+ /*
+ * Lookup the algorithm description maintained by xfrm_algo,
+ * verify crypto transform properties, and store information
+ * we need for AH processing. This lookup cannot fail here
+ * after a successful crypto_alloc_tfm().
+ */
+ aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
+ BUG_ON(!aalg_desc);
+
+ if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+ crypto_tfm_alg_digestsize(ahp->tfm)) {
+ printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
+ x->aalg->alg_name, crypto_tfm_alg_digestsize(ahp->tfm),
+ aalg_desc->uinfo.auth.icv_fullbits/8);
+ goto error;
+ }
+
+ ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
+ ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
+
+ ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
+ if (!ahp->work_icv)
+ goto error;
+
+ x->props.header_len = XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV);
+ if (x->props.mode)
+ x->props.header_len += 20;
+ x->data = ahp;
+
+ return 0;
+
+error:
+ if (ahp) {
+ if (ahp->work_icv)
+ kfree(ahp->work_icv);
+ if (ahp->tfm)
+ crypto_free_tfm(ahp->tfm);
+ kfree(ahp);
+ }
+ return -EINVAL;
+}
+
+static void ah6_destroy(struct xfrm_state *x)
+{
+ struct ah_data *ahp = x->data;
+
+ if (ahp->work_icv) {
+ kfree(ahp->work_icv);
+ ahp->work_icv = NULL;
+ }
+ if (ahp->tfm) {
+ crypto_free_tfm(ahp->tfm);
+ ahp->tfm = NULL;
+ }
+}
+
+static struct xfrm_type ah6_type =
+{
+ .description = "AH6",
+ .proto = IPPROTO_AH,
+ .init_state = ah6_init_state,
+ .destructor = ah6_destroy,
+ .input = ah6_input,
+ .output = ah6_output
+};
+
+static struct inet6_protocol ah6_protocol = {
+ .handler = xfrm6_rcv,
+ .err_handler = ah6_err,
+};
+
+int __init ah6_init(void)
+{
+ SET_MODULE_OWNER(&ah6_type);
+
+ if (xfrm6_register_type(&ah6_type) < 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);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+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)
+ printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n");
+
+}
+
+module_init(ah6_init);
+module_exit(ah6_fini);
+
+MODULE_LICENSE("GPL");
diff -ruN -x CVS linux-2.5.63/net/ipv6/esp6.c linux25/net/ipv6/esp6.c
--- linux-2.5.63/net/ipv6/esp6.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25/net/ipv6/esp6.c 2003-03-05 11:33:20.000000000 +0900
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C)2002 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors
+ *
+ * Mitsuru KANDA @USAGI : IPv6 Support
+ * Kazunori MIYAZAWA @USAGI :
+ * Kunihiro Ishiguro :
+ *
+ * This file is derived from net/ipv4/esp.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <linux/random.h>
+#include <net/icmp.h>
+#include <net/ipv6.h>
+#include <linux/icmpv6.h>
+
+#define MAX_SG_ONSTACK 4
+
+/* BUGS:
+ * - we assume replay seqno is always present.
+ */
+
+/* Move to common area: it is shared with AH. */
+/* Common with AH after some work on arguments. */
+
+/* XXX no ipv6 esp specific */
+#define NIP6(addr) \
+ ntohs((addr).s6_addr16[0]),\
+ ntohs((addr).s6_addr16[1]),\
+ ntohs((addr).s6_addr16[2]),\
+ ntohs((addr).s6_addr16[3]),\
+ ntohs((addr).s6_addr16[4]),\
+ ntohs((addr).s6_addr16[5]),\
+ ntohs((addr).s6_addr16[6]),\
+ ntohs((addr).s6_addr16[7])
+
+static int get_offset(u8 *packet, u32 packet_len, u8 *nexthdr, struct ipv6_opt_hdr **prevhdr)
+{
+ u16 offset = sizeof(struct ipv6hdr);
+ struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ u8 nextnexthdr;
+
+ *nexthdr = ((struct ipv6hdr*)packet)->nexthdr;
+
+ while (offset + 1 < packet_len) {
+
+ switch (*nexthdr) {
+
+ case NEXTHDR_HOP:
+ case NEXTHDR_ROUTING:
+ offset += ipv6_optlen(exthdr);
+ *nexthdr = exthdr->nexthdr;
+ *prevhdr = exthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ case NEXTHDR_DEST:
+ nextnexthdr =
+ ((struct ipv6_opt_hdr*)(packet + offset + ipv6_optlen(exthdr)))->nexthdr;
+ /* XXX We know the option is inner dest opt
+ with next next header check. */
+ if (nextnexthdr != NEXTHDR_HOP &&
+ nextnexthdr != NEXTHDR_ROUTING &&
+ nextnexthdr != NEXTHDR_DEST) {
+ return offset;
+ }
+ offset += ipv6_optlen(exthdr);
+ *nexthdr = exthdr->nexthdr;
+ *prevhdr = exthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ default :
+ return offset;
+ }
+ }
+
+ return offset;
+}
+
+int esp6_output(struct sk_buff *skb)
+{
+ int err;
+ int hdr_len = 0;
+ struct dst_entry *dst = skb->dst;
+ struct xfrm_state *x = dst->xfrm;
+ struct ipv6hdr *iph = NULL, *top_iph;
+ struct ip_esp_hdr *esph;
+ struct crypto_tfm *tfm;
+ struct esp_data *esp;
+ struct sk_buff *trailer;
+ struct ipv6_opt_hdr *prevhdr = NULL;
+ int blksize;
+ int clen;
+ int alen;
+ int nfrags;
+ u8 nexthdr;
+printk(KERN_DEBUG "%s\n", __FUNCTION__);
+ /* First, if the skb is not checksummed, complete checksum. */
+ if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL)
+ return -EINVAL;
+
+ spin_lock_bh(&x->lock);
+ if ((err = xfrm_state_check_expire(x)) != 0)
+ goto error;
+ if ((err = xfrm_state_check_space(x, skb)) != 0)
+ goto error;
+
+ err = -ENOMEM;
+
+ /* Strip IP header in transport mode. Save it. */
+
+ if (!x->props.mode) {
+ hdr_len = get_offset(skb->nh.raw, skb->len, &nexthdr, &prevhdr);
+ iph = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!iph) {
+ err = -ENOMEM;
+ goto error;
+ }
+ memcpy(iph, skb->nh.raw, hdr_len);
+ __skb_pull(skb, hdr_len);
+ }
+
+ /* Now skb is pure payload to encrypt */
+
+ /* Round to block size */
+ clen = skb->len;
+
+ esp = x->data;
+ alen = esp->auth.icv_trunc_len;
+ tfm = esp->conf.tfm;
+ blksize = crypto_tfm_alg_blocksize(tfm);
+ clen = (clen + 2 + blksize-1)&~(blksize-1);
+ if (esp->conf.padlen)
+ clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
+
+ if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0) {
+ if (!x->props.mode && iph) kfree(iph);
+ goto error;
+ }
+
+ /* Fill padding... */
+ do {
+ int i;
+ for (i=0; i<clen-skb->len - 2; i++)
+ *(u8*)(trailer->tail + i) = i+1;
+ } while (0);
+ *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
+ pskb_put(skb, trailer, clen - skb->len);
+
+ if (x->props.mode) {
+ iph = skb->nh.ipv6h;
+ top_iph = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+ esph = (struct ip_esp_hdr*)(top_iph+1);
+ *(u8*)(trailer->tail - 1) = IPPROTO_IPV6;
+ top_iph->version = 6;
+ top_iph->priority = iph->priority;
+ top_iph->flow_lbl[0] = iph->flow_lbl[0];
+ top_iph->flow_lbl[1] = iph->flow_lbl[1];
+ top_iph->flow_lbl[2] = iph->flow_lbl[2];
+ top_iph->nexthdr = IPPROTO_ESP;
+ top_iph->payload_len = htons(skb->len + alen);
+ top_iph->hop_limit = iph->hop_limit;
+ memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct ipv6hdr));
+ memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct ipv6hdr));
+ } else {
+ /* XXX exthdr */
+ esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
+ skb->h.raw = (unsigned char*)esph;
+ top_iph = (struct ipv6hdr*)skb_push(skb, hdr_len);
+ memcpy(top_iph, iph, hdr_len);
+ kfree(iph);
+ top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
+ if (prevhdr) {
+ prevhdr->nexthdr = IPPROTO_ESP;
+ } else {
+ top_iph->nexthdr = IPPROTO_ESP;
+ }
+ *(u8*)(trailer->tail - 1) = nexthdr;
+ }
+
+ esph->spi = x->id.spi;
+ esph->seq_no = htonl(++x->replay.oseq);
+
+ if (esp->conf.ivlen)
+ crypto_cipher_set_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm));
+
+ do {
+ struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags];
+ struct scatterlist *sg = sgbuf;
+
+ if (unlikely(nfrags > MAX_SG_ONSTACK)) {
+ sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
+ if (!sg)
+ goto error;
+ }
+ skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+ crypto_cipher_encrypt(tfm, sg, sg, clen);
+ if (unlikely(sg != sgbuf))
+ kfree(sg);
+ } while (0);
+
+ if (esp->conf.ivlen) {
+ memcpy(esph->enc_data, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm));
+ crypto_cipher_get_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm));
+ }
+
+ if (esp->auth.icv_full_len) {
+ esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+ 8+esp->conf.ivlen+clen, trailer->tail);
+ pskb_put(skb, trailer, alen);
+ }
+
+ skb->nh.raw = skb->data;
+
+ x->curlft.bytes += skb->len;
+ x->curlft.packets++;
+ spin_unlock_bh(&x->lock);
+ if ((skb->dst = dst_pop(dst)) == NULL)
+ goto error_nolock;
+ return NET_XMIT_BYPASS;
+
+error:
+ spin_unlock_bh(&x->lock);
+error_nolock:
+ kfree_skb(skb);
+ return err;
+}
+
+int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct ipv6hdr *iph;
+ struct ip_esp_hdr *esph;
+ struct esp_data *esp = x->data;
+ struct sk_buff *trailer;
+ int blksize = crypto_tfm_alg_blocksize(esp->conf.tfm);
+ int alen = esp->auth.icv_trunc_len;
+ int elen = skb->len - 8 - esp->conf.ivlen - alen;
+
+ int hdr_len = skb->h.raw - skb->nh.raw;
+ int nfrags;
+ u8 ret_nexthdr = 0;
+ unsigned char *tmp_hdr = NULL;
+
+ if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
+ goto out;
+
+ if (elen <= 0 || (elen & (blksize-1)))
+ goto out;
+
+ tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!tmp_hdr)
+ goto out;
+ memcpy(tmp_hdr, skb->nh.raw, hdr_len);
+
+ /* If integrity check is required, do this. */
+ if (esp->auth.icv_full_len) {
+ u8 sum[esp->auth.icv_full_len];
+ u8 sum1[alen];
+
+ esp->auth.icv(esp, skb, 0, skb->len-alen, sum);
+
+ if (skb_copy_bits(skb, skb->len-alen, sum1, alen))
+ BUG();
+
+ if (unlikely(memcmp(sum, sum1, alen))) {
+ x->stats.integrity_failed++;
+ goto out;
+ }
+ }
+
+ if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
+ goto out;
+
+ skb->ip_summed = CHECKSUM_NONE;
+
+ esph = (struct ip_esp_hdr*)skb->data;
+ iph = skb->nh.ipv6h;
+
+ /* Get ivec. This can be wrong, check against another impls. */
+ if (esp->conf.ivlen)
+ crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, crypto_tfm_alg_ivsize(esp->conf.tfm));
+
+ {
+ u8 nexthdr[2];
+ struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags];
+ struct scatterlist *sg = sgbuf;
+ u8 padlen;
+
+ if (unlikely(nfrags > MAX_SG_ONSTACK)) {
+ sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
+ if (!sg)
+ goto out;
+ }
+ skb_to_sgvec(skb, sg, 8+esp->conf.ivlen, elen);
+ crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen);
+ if (unlikely(sg != sgbuf))
+ kfree(sg);
+
+ if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
+ BUG();
+
+ padlen = nexthdr[0];
+ if (padlen+2 >= elen) {
+ if (net_ratelimit()) {
+ printk(KERN_WARNING "ipsec esp packet is garbage padlen=%d, elen=%d\n", padlen+2, elen);
+ }
+ goto out;
+ }
+ /* ... check padding bits here. Silly. :-) */
+
+ ret_nexthdr = nexthdr[1];
+ pskb_trim(skb, skb->len - alen - padlen - 2);
+ skb->h.raw = skb_pull(skb, 8 + esp->conf.ivlen);
+ skb->nh.raw += 8 + esp->conf.ivlen;
+ memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+ }
+ kfree(tmp_hdr);
+ return ret_nexthdr;
+
+out:
+ return -EINVAL;
+}
+
+static u32 esp6_get_max_size(struct xfrm_state *x, int mtu)
+{
+ struct esp_data *esp = x->data;
+ u32 blksize = crypto_tfm_alg_blocksize(esp->conf.tfm);
+
+ if (x->props.mode) {
+ mtu = (mtu + 2 + blksize-1)&~(blksize-1);
+ } else {
+ /* The worst case. */
+ mtu += 2 + blksize;
+ }
+ if (esp->conf.padlen)
+ mtu = (mtu + esp->conf.padlen-1)&~(esp->conf.padlen-1);
+
+ return mtu + x->props.header_len + esp->auth.icv_full_len;
+}
+
+void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
+ struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+offset);
+ struct xfrm_state *x;
+
+ if (type != ICMPV6_DEST_UNREACH ||
+ type != ICMPV6_PKT_TOOBIG)
+ return;
+
+ x = xfrm6_state_lookup(&iph->daddr, esph->spi, IPPROTO_ESP);
+ if (!x)
+ return;
+ printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/"
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ ntohl(esph->spi), NIP6(iph->daddr));
+ xfrm_state_put(x);
+}
+
+void esp6_destroy(struct xfrm_state *x)
+{
+ struct esp_data *esp = x->data;
+
+ if (esp->conf.tfm) {
+ crypto_free_tfm(esp->conf.tfm);
+ esp->conf.tfm = NULL;
+ }
+ if (esp->conf.ivec) {
+ kfree(esp->conf.ivec);
+ esp->conf.ivec = NULL;
+ }
+ if (esp->auth.tfm) {
+ crypto_free_tfm(esp->auth.tfm);
+ esp->auth.tfm = NULL;
+ }
+ if (esp->auth.work_icv) {
+ kfree(esp->auth.work_icv);
+ esp->auth.work_icv = NULL;
+ }
+}
+
+int esp6_init_state(struct xfrm_state *x, void *args)
+{
+ struct esp_data *esp = NULL;
+
+ if (x->aalg) {
+ if (x->aalg->alg_key_len == 0 || x->aalg->alg_key_len > 512)
+ goto error;
+ }
+ if (x->ealg == NULL || x->ealg->alg_key_len == 0)
+ goto error;
+
+ esp = kmalloc(sizeof(*esp), GFP_KERNEL);
+ if (esp == NULL)
+ return -ENOMEM;
+
+ memset(esp, 0, sizeof(*esp));
+
+ if (x->aalg) {
+ struct xfrm_algo_desc *aalg_desc;
+
+ esp->auth.key = x->aalg->alg_key;
+ esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
+ esp->auth.tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
+ if (esp->auth.tfm == NULL)
+ goto error;
+ esp->auth.icv = esp_hmac_digest;
+
+ aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
+ BUG_ON(!aalg_desc);
+
+ if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+ crypto_tfm_alg_digestsize(esp->auth.tfm)) {
+ printk(KERN_INFO "ESP: %s digestsize %u != %hu\n",
+ x->aalg->alg_name,
+ crypto_tfm_alg_digestsize(esp->auth.tfm),
+ aalg_desc->uinfo.auth.icv_fullbits/8);
+ goto error;
+ }
+
+ esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
+ esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
+
+ esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
+ if (!esp->auth.work_icv)
+ goto error;
+ }
+ esp->conf.key = x->ealg->alg_key;
+ esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
+ esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, CRYPTO_TFM_MODE_CBC);
+ if (esp->conf.tfm == NULL)
+ goto error;
+ esp->conf.ivlen = crypto_tfm_alg_ivsize(esp->conf.tfm);
+ esp->conf.padlen = 0;
+ if (esp->conf.ivlen) {
+ esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
+ get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
+ }
+ crypto_cipher_setkey(esp->conf.tfm, esp->conf.key, esp->conf.key_len);
+ x->props.header_len = 8 + esp->conf.ivlen;
+ if (x->props.mode)
+ x->props.header_len += 40; /* XXX ext hdr */
+ x->data = esp;
+ return 0;
+
+error:
+ if (esp) {
+ if (esp->auth.tfm)
+ crypto_free_tfm(esp->auth.tfm);
+ if (esp->auth.work_icv)
+ kfree(esp->auth.work_icv);
+ if (esp->conf.tfm)
+ crypto_free_tfm(esp->conf.tfm);
+ kfree(esp);
+ }
+ return -EINVAL;
+}
+
+static struct xfrm_type esp6_type =
+{
+ .description = "ESP6",
+ .proto = IPPROTO_ESP,
+ .init_state = esp6_init_state,
+ .destructor = esp6_destroy,
+ .get_max_size = esp6_get_max_size,
+ .input = esp6_input,
+ .output = esp6_output
+};
+
+static struct inet6_protocol esp6_protocol = {
+ .handler = xfrm6_rcv,
+ .err_handler = esp6_err,
+};
+
+int __init esp6_init(void)
+{
+ SET_MODULE_OWNER(&esp6_type);
+ if (xfrm6_register_type(&esp6_type) < 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);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+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)
+ printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
+}
+
+module_init(esp6_init);
+module_exit(esp6_fini);
+
+MODULE_LICENSE("GPL");
diff -ruN -x CVS linux-2.5.63/net/ipv6/ip6_input.c linux25/net/ipv6/ip6_input.c
--- linux-2.5.63/net/ipv6/ip6_input.c 2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/ip6_input.c 2003-03-04 20:38:16.000000000 +0900
@@ -150,7 +150,8 @@
It would be stupid to detect for optional headers,
which are missing with probability of 200%
*/
- if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
+ if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP &&
+ nexthdr != NEXTHDR_AUTH && nexthdr != NEXTHDR_ESP) {
nhoff = ipv6_parse_exthdrs(&skb, nhoff);
if (nhoff < 0)
return 0;
diff -ruN -x CVS linux-2.5.63/net/ipv6/ip6_output.c linux25/net/ipv6/ip6_output.c
--- linux-2.5.63/net/ipv6/ip6_output.c 2003-02-25 04:05:06.000000000 +0900
+++ linux25/net/ipv6/ip6_output.c 2003-03-04 20:38:16.000000000 +0900
@@ -192,6 +192,11 @@
int seg_len = skb->len;
int hlimit;
u32 mtu;
+ int err = 0;
+
+ if ((err = xfrm_lookup(&skb->dst, fl, sk, 0)) < 0) {
+ return err;
+ }
if (opt) {
int head_room;
@@ -576,6 +581,13 @@
}
pktlength = length;
+ if (dst) {
+ if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) {
+ dst_release(dst);
+ return -ENETUNREACH;
+ }
+ }
+
if (hlimit < 0) {
if (ipv6_addr_is_multicast(fl->fl6_dst))
hlimit = np->mcast_hops;
@@ -630,10 +642,8 @@
err = 0;
if (flags&MSG_PROBE)
goto out;
-
- skb = sock_alloc_send_skb(sk, pktlength + 15 +
- dev->hard_header_len,
- flags & MSG_DONTWAIT, &err);
+ /* alloc skb with mtu as we do in the IPv4 stack for IPsec */
+ skb = sock_alloc_send_skb(sk, mtu, flags & MSG_DONTWAIT, &err);
if (skb == NULL) {
IP6_INC_STATS(Ip6OutDiscards);
@@ -663,6 +673,8 @@
err = getfrag(data, &hdr->saddr,
((char *) hdr) + (pktlength - length),
0, length);
+ if (!opt || !opt->dst1opt)
+ skb->h.raw = ((char *) hdr) + (pktlength - length);
if (!err) {
IP6_INC_STATS(Ip6OutRequests);
diff -ruN -x CVS linux-2.5.63/net/ipv6/ndisc.c linux25/net/ipv6/ndisc.c
--- linux-2.5.63/net/ipv6/ndisc.c 2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv6/ndisc.c 2003-03-05 11:30:41.000000000 +0900
@@ -71,6 +71,7 @@
#include <net/addrconf.h>
#include <net/icmp.h>
+#include <net/flow.h>
#include <net/checksum.h>
#include <linux/proc_fs.h>
@@ -335,8 +336,6 @@
unsigned char ha[MAX_ADDR_LEN];
unsigned char *h_dest = NULL;
- skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
if (dev->hard_header) {
if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
ndisc_mc_map(daddr, ha, dev, 1);
@@ -373,10 +372,50 @@
* Send a Neighbour Advertisement
*/
+int ndisc_output(struct sk_buff *skb)
+{
+ if (skb) {
+ struct neighbour *neigh = (skb->dst ? skb->dst->neighbour : NULL);
+ if (ndisc_build_ll_hdr(skb, skb->dev, &skb->nh.ipv6h->daddr, neigh, skb->len) == 0) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ dev_queue_xmit(skb);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static inline void ndisc_rt_init(struct rt6_info *rt, struct net_device *dev,
+ struct neighbour *neigh)
+{
+ rt->rt6i_dev = dev;
+ rt->rt6i_nexthop = neigh;
+ rt->rt6i_expires = 0;
+ rt->rt6i_flags = RTF_LOCAL;
+ rt->rt6i_metric = 0;
+ rt->rt6i_hoplimit = 255;
+ rt->u.dst.output = ndisc_output;
+}
+
+static inline void ndisc_flow_init(struct flowi *fl, u8 type,
+ struct in6_addr *saddr, struct in6_addr *daddr)
+{
+ memset(fl, 0, sizeof(*fl));
+ fl->fl6_src = saddr;
+ fl->fl6_dst = daddr;
+ fl->proto = IPPROTO_ICMPV6;
+ fl->uli_u.icmpt.type = type;
+ fl->uli_u.icmpt.code = 0;
+}
+
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
struct in6_addr *daddr, struct in6_addr *solicited_addr,
- int router, int solicited, int override, int inc_opt)
+ int router, int solicited, int override, int inc_opt)
{
+ struct flowi fl;
+ struct rt6_info *rt = NULL;
+ struct dst_entry* dst;
struct sock *sk = ndisc_socket->sk;
struct nd_msg *msg;
int len;
@@ -385,6 +424,22 @@
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+ rt = ndisc_get_dummy_rt();
+ if (!rt)
+ return;
+
+ ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, solicited_addr, daddr);
+ ndisc_rt_init(rt, dev, neigh);
+
+ dst = (struct dst_entry*)rt;
+ dst_clone(dst);
+
+ err = xfrm_lookup(&dst, &fl, NULL, 0);
+ if (err < 0) {
+ dst_release(dst);
+ return;
+ }
+
if (inc_opt) {
if (dev->addr_len)
len += NDISC_OPT_SPACE(dev->addr_len);
@@ -400,14 +455,10 @@
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
- kfree_skb(skb);
- return;
- }
-
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
- msg = (struct nd_msg *) skb_put(skb, len);
+ skb->h.raw = (unsigned char*) msg = (struct nd_msg *) skb_put(skb, len);
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
msg->icmph.icmp6_code = 0;
@@ -430,7 +481,9 @@
csum_partial((__u8 *) msg,
len, 0));
- dev_queue_xmit(skb);
+ dst_clone(dst);
+ skb->dst = dst;
+ dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -440,6 +493,9 @@
struct in6_addr *solicit,
struct in6_addr *daddr, struct in6_addr *saddr)
{
+ struct flowi fl;
+ struct rt6_info *rt = NULL;
+ struct dst_entry* dst;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
@@ -454,6 +510,22 @@
saddr = &addr_buf;
}
+ rt = ndisc_get_dummy_rt();
+ if (!rt)
+ return;
+
+ ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);
+ ndisc_rt_init(rt, dev, neigh);
+
+ dst = (struct dst_entry*)rt;
+ dst_clone(dst);
+
+ err = xfrm_lookup(&dst, &fl, NULL, 0);
+ if (err < 0) {
+ dst_release(dst);
+ return;
+ }
+
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
send_llinfo = dev->addr_len && ipv6_addr_type(saddr) != IPV6_ADDR_ANY;
if (send_llinfo)
@@ -466,14 +538,10 @@
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
- kfree_skb(skb);
- return;
- }
-
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- msg = (struct nd_msg *)skb_put(skb, len);
+ skb->h.raw = (unsigned char*) msg = (struct nd_msg *)skb_put(skb, len);
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
msg->icmph.icmp6_code = 0;
msg->icmph.icmp6_cksum = 0;
@@ -492,7 +560,9 @@
csum_partial((__u8 *) msg,
len, 0));
/* send it! */
- dev_queue_xmit(skb);
+ dst_clone(dst);
+ skb->dst = dst;
+ dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -501,6 +571,9 @@
void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr)
{
+ struct flowi fl;
+ struct rt6_info *rt = NULL;
+ struct dst_entry* dst;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
@@ -508,6 +581,22 @@
int len;
int err;
+ rt = ndisc_get_dummy_rt();
+ if (!rt)
+ return;
+
+ ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr);
+ ndisc_rt_init(rt, dev, NULL);
+
+ dst = (struct dst_entry*)rt;
+ dst_clone(dst);
+
+ err = xfrm_lookup(&dst, &fl, NULL, 0);
+ if (err < 0) {
+ dst_release(dst);
+ return;
+ }
+
len = sizeof(struct icmp6hdr);
if (dev->addr_len)
len += NDISC_OPT_SPACE(dev->addr_len);
@@ -519,14 +608,10 @@
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {
- kfree_skb(skb);
- return;
- }
-
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- hdr = (struct icmp6hdr *) skb_put(skb, len);
+ skb->h.raw = (unsigned char*) hdr = (struct icmp6hdr *) skb_put(skb, len);
hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
hdr->icmp6_code = 0;
hdr->icmp6_cksum = 0;
@@ -543,7 +628,9 @@
csum_partial((__u8 *) hdr, len, 0));
/* send it! */
- dev_queue_xmit(skb);
+ dst_clone(dst);
+ skb->dst = dst;
+ dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRouterSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -1125,6 +1212,8 @@
struct in6_addr *addrp;
struct net_device *dev;
struct rt6_info *rt;
+ struct dst_entry *dst;
+ struct flowi fl;
u8 *opt;
int rd_len;
int err;
@@ -1136,6 +1225,22 @@
if (rt == NULL)
return;
+ dst = (struct dst_entry*)rt;
+
+ if (ipv6_get_lladdr(dev, &saddr_buf)) {
+ ND_PRINTK1("redirect: no link_local addr for dev\n");
+ return;
+ }
+
+ ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr);
+
+ dst_clone(dst);
+ err = xfrm_lookup(&dst, &fl, NULL, 0);
+ if (err) {
+ dst_release(dst);
+ return;
+ }
+
if (rt->rt6i_flags & RTF_GATEWAY) {
ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
dst_release(&rt->u.dst);
@@ -1164,11 +1269,6 @@
rd_len &= ~0x7;
len += rd_len;
- if (ipv6_get_lladdr(dev, &saddr_buf)) {
- ND_PRINTK1("redirect: no link_local addr for dev\n");
- return;
- }
-
buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
0, &err);
if (buff == NULL) {
@@ -1178,15 +1278,11 @@
hlen = 0;
- if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 0) {
- kfree_skb(buff);
- return;
- }
-
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
IPPROTO_ICMPV6, len);
- icmph = (struct icmp6hdr *) skb_put(buff, len);
+ skb->h.raw = (unsigned char*) icmph = (struct icmp6hdr *) skb_put(buff, len);
memset(icmph, 0, sizeof(struct icmp6hdr));
icmph->icmp6_type = NDISC_REDIRECT;
@@ -1224,7 +1320,8 @@
len, IPPROTO_ICMPV6,
csum_partial((u8 *) icmph, len, 0));
- dev_queue_xmit(buff);
+ skb->dst = dst;
+ dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRedirects);
ICMP6_INC_STATS(Icmp6OutMsgs);
diff -ruN -x CVS linux-2.5.63/net/ipv6/raw.c linux25/net/ipv6/raw.c
--- linux-2.5.63/net/ipv6/raw.c 2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/ipv6/raw.c 2003-03-04 20:38:16.000000000 +0900
@@ -45,6 +45,7 @@
#include <net/inet_common.h>
#include <net/rawv6.h>
+#include <net/xfrm.h>
struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
@@ -304,6 +305,11 @@
struct inet_opt *inet = inet_sk(sk);
struct raw6_opt *raw_opt = raw6_sk(sk);
+ if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
if (!raw_opt->checksum)
skb->ip_summed = CHECKSUM_UNNECESSARY;
diff -ruN -x CVS linux-2.5.63/net/ipv6/route.c linux25/net/ipv6/route.c
--- linux-2.5.63/net/ipv6/route.c 2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/route.c 2003-03-05 11:30:41.000000000 +0900
@@ -49,6 +49,8 @@
#include <net/addrconf.h>
#include <net/tcp.h>
#include <linux/rtnetlink.h>
+#include <net/dst.h>
+#include <net/xfrm.h>
#include <asm/uaccess.h>
@@ -128,6 +130,12 @@
rwlock_t rt6_lock = RW_LOCK_UNLOCKED;
+/* Dummy rt for ndisc */
+struct rt6_info *ndisc_get_dummy_rt()
+{
+ return dst_alloc(&ip6_dst_ops);
+}
+
/*
* Route lookup. Any rt6_lock is implied.
*/
@@ -1809,6 +1817,14 @@
#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)
{
@@ -1817,6 +1833,7 @@
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);
@@ -1830,7 +1847,7 @@
proc_net_remove("ipv6_route");
proc_net_remove("rt6_stats");
#endif
-
+ xfrm_dst_lookup_unregister(AF_INET6);
rt6_ifdown(NULL);
fib6_gc_cleanup();
}
diff -ruN -x CVS linux-2.5.63/net/ipv6/tcp_ipv6.c linux25/net/ipv6/tcp_ipv6.c
--- linux-2.5.63/net/ipv6/tcp_ipv6.c 2003-02-25 04:05:33.000000000 +0900
+++ linux25/net/ipv6/tcp_ipv6.c 2003-03-04 20:38:16.000000000 +0900
@@ -50,6 +50,7 @@
#include <net/ip6_route.h>
#include <net/inet_ecn.h>
#include <net/protocol.h>
+#include <net/xfrm.h>
#include <asm/uaccess.h>
@@ -677,6 +678,9 @@
fl.nl_u.ip6_u.daddr = rt0->addr;
}
+ if (!fl.fl6_src)
+ fl.fl6_src = &np->saddr;
+
dst = ip6_route_output(sk, &fl);
if ((err = dst->error) != 0) {
@@ -1637,6 +1641,9 @@
if (sk_filter(sk, skb, 0))
goto discard_and_relse;
+ if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+ goto discard_it;
+
skb->dev = NULL;
bh_lock_sock(sk);
@@ -1652,6 +1659,9 @@
return ret;
no_tcp_socket:
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto discard_and_relse;
+
if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
bad_packet:
TCP_INC_STATS_BH(TcpInErrs);
@@ -1671,8 +1681,11 @@
discard_and_relse:
sock_put(sk);
goto discard_it;
-
+
do_time_wait:
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto discard_and_relse;
+
if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
TCP_INC_STATS_BH(TcpInErrs);
sock_put(sk);
diff -ruN -x CVS linux-2.5.63/net/ipv6/udp.c linux25/net/ipv6/udp.c
--- linux-2.5.63/net/ipv6/udp.c 2003-02-25 04:05:40.000000000 +0900
+++ linux25/net/ipv6/udp.c 2003-03-04 20:38:16.000000000 +0900
@@ -50,6 +50,7 @@
#include <net/inet_common.h>
#include <net/checksum.h>
+#include <net/xfrm.h>
DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6);
@@ -541,6 +542,11 @@
static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
{
+ if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
+ kfree_skb(skb);
+ return -1;
+ }
+
#if defined(CONFIG_FILTER)
if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
@@ -646,6 +652,9 @@
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
goto short_packet;
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto discard;
+
saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr;
uh = skb->h.uh;
diff -ruN -x CVS linux-2.5.63/net/key/af_key.c linux25/net/key/af_key.c
--- linux-2.5.63/net/key/af_key.c 2003-02-25 04:05:13.000000000 +0900
+++ linux25/net/key/af_key.c 2003-03-04 20:38:16.000000000 +0900
@@ -550,8 +550,8 @@
switch (((struct sockaddr *)(addr + 1))->sa_family) {
case AF_INET:
- x = xfrm_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
- sa->sadb_sa_spi, proto);
+ x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
+ sa->sadb_sa_spi, proto);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
@@ -1097,18 +1097,7 @@
min_spi = htonl(0x100);
max_spi = htonl(0x0fffffff);
}
- switch (x->props.family) {
- case AF_INET:
- xfrm_alloc_spi(x, min_spi, max_spi);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- xfrm6_alloc_spi(x, min_spi, max_spi);
- break;
-#endif
- default:
- break;
- }
+ xfrm_alloc_spi(x, min_spi, max_spi);
if (x->id.spi)
resp_skb = pfkey_xfrm_state2msg(x, 0, 3);
}
diff -ruN -x CVS linux-2.5.63/net/netsyms.c linux25/net/netsyms.c
--- linux-2.5.63/net/netsyms.c 2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/netsyms.c 2003-03-04 20:38:15.000000000 +0900
@@ -296,11 +296,11 @@
EXPORT_SYMBOL(__xfrm_route_forward);
EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy);
-EXPORT_SYMBOL(xfrm_state_find);
+EXPORT_SYMBOL(xfrm4_state_find);
EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space);
-EXPORT_SYMBOL(xfrm_state_lookup);
+EXPORT_SYMBOL(xfrm4_state_lookup);
EXPORT_SYMBOL(xfrm_replay_check);
EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors);
@@ -324,13 +324,17 @@
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_alloc_spi);
EXPORT_SYMBOL(xfrm6_register_type);
EXPORT_SYMBOL(xfrm6_unregister_type);
EXPORT_SYMBOL(xfrm6_get_type);
+EXPORT_SYMBOL(xfrm6_clear_mutable_options);
#endif
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
@@ -342,6 +346,15 @@
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);
EXPORT_SYMBOL_GPL(xfrm_ealg_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
+#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
+EXPORT_SYMBOL_GPL(skb_cow_data);
+EXPORT_SYMBOL_GPL(pskb_put);
+EXPORT_SYMBOL_GPL(skb_icv_walk);
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+#endif
#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
/* inet functions common to v4 and v6 */
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATH] IPv6 IPsec support
2003-03-05 14:30 [PATH] IPv6 IPsec support Kazunori Miyazawa
@ 2003-03-05 15:21 ` David S. Miller
2003-03-05 15:48 ` (usagi-core 12294) Re: [PATCH] " YOSHIFUJI Hideaki / 吉藤英明
2003-03-05 23:25 ` [PATH] " David S. Miller
1 sibling, 1 reply; 10+ messages in thread
From: David S. Miller @ 2003-03-05 15:21 UTC (permalink / raw)
To: kazunori; +Cc: kuznet, linux-kernel, netdev, usagi-core
From: Kazunori Miyazawa <kazunori@miyazawa.org>
Date: Wed, 5 Mar 2003 23:30:25 +0900
Hello Miyazawa-san,
I submit the patch to let the kernel support ipv6 ipsec again.
It is able to comple ipv6 as module.
This patch incldes a couple of clean-up and
changes of function name.
Excellent work.
I have comments, but they are very minor and can wait.
I will apply your patch after basic build testing.
The next large task will be to abstract out more common
pieces of code. There is still quite a bit of code duplication
between v4 and v6 xfrm methods,
Thank you!
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: (usagi-core 12294) Re: [PATCH] IPv6 IPsec support
2003-03-05 15:21 ` David S. Miller
@ 2003-03-05 15:48 ` YOSHIFUJI Hideaki / 吉藤英明
2003-03-05 23:41 ` David S. Miller
0 siblings, 1 reply; 10+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2003-03-05 15:48 UTC (permalink / raw)
To: davem; +Cc: kazunori, kuznet, linux-kernel, netdev, usagi
In article <20030305.072149.121185037.davem@redhat.com> (at Wed, 05 Mar 2003 07:21:49 -0800 (PST)), "David S. Miller" <davem@redhat.com> says:
> I will apply your patch after basic build testing.
Thank you.
> The next large task will be to abstract out more common
> pieces of code. There is still quite a bit of code duplication
> between v4 and v6 xfrm methods,
Yes, we will do that. That patch is first step for reducing
duplicate codes between IPv4 and IPv6.
--yoshfuji
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: (usagi-core 12294) Re: [PATCH] IPv6 IPsec support
2003-03-05 15:48 ` (usagi-core 12294) Re: [PATCH] " YOSHIFUJI Hideaki / 吉藤英明
@ 2003-03-05 23:41 ` David S. Miller
[not found] ` <20030306213217.GA6358@f00f.org>
0 siblings, 1 reply; 10+ messages in thread
From: David S. Miller @ 2003-03-05 23:41 UTC (permalink / raw)
To: yoshfuji; +Cc: kazunori, kuznet, linux-kernel, netdev, usagi
From: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Date: Thu, 06 Mar 2003 00:48:20 +0900 (JST)
> The next large task will be to abstract out more common
> pieces of code. There is still quite a bit of code duplication
> between v4 and v6 xfrm methods,
Yes, we will do that. That patch is first step for reducing
duplicate codes between IPv4 and IPv6.
Great. I believe it should be possible, in the end, to make the XFRM
engine %100 address-family (v4, v6 etc.) and protocol (ah, esp)
independant. If that goal is achieved, we may move generic parts from
net/ipv4/xfrm_*.c to net/xfrm_*.c
Note that this coincides with the idea to eventually have
an address-family independant flow cache.
Most of the address-family specific areas are:
1) DST lookup (xfrm_dst_lookup_t)
2) selector key comparisons and state lookup
(xfrm$(AF)_selector_match, xfrm$(AF)_state_find)
3) receive processing (xfrm${AF}_rcv)
#1 is made for ipv6 by Miyazawa-san's patch. This could logically
be extended to handle issues #2 and #3 above.
All protocol specific (ESP, AH) and address-family specific references
should go away from places like include/net/xfrm.h
I think you understand all of this, and therefore I cannot wait for the
next ipsec cleanup patch from you :)
Finally, note that eventually we will need some reference counting scheme
for to allow xfrm address-family modules to be unloaded safely.
Currently, ipv4 cannot be a module and ipv6 as a module is not able
to unload :-) So the module unload problem does not exist right
at this moment. So ignore this issue for now.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATH] IPv6 IPsec support
2003-03-05 14:30 [PATH] IPv6 IPsec support Kazunori Miyazawa
2003-03-05 15:21 ` David S. Miller
@ 2003-03-05 23:25 ` David S. Miller
2003-03-06 0:32 ` Kazunori Miyazawa
1 sibling, 1 reply; 10+ messages in thread
From: David S. Miller @ 2003-03-05 23:25 UTC (permalink / raw)
To: kazunori; +Cc: kuznet, linux-kernel, netdev, usagi-core
From: Kazunori Miyazawa <kazunori@miyazawa.org>
Date: Wed, 5 Mar 2003 23:30:25 +0900
Hello Miyazawa-san,
I submit the patch to let the kernel support ipv6 ipsec again.
It is able to comple ipv6 as module.
As promised I applied the patch. I will push it to Linus later
this evening, or tomorrow.
In this initial checkin I made only 2 minor fixes, they
are attached below:
--- ./include/net/ip6_route.h.~1~ Wed Mar 5 15:32:41 2003
+++ ./include/net/ip6_route.h Wed Mar 5 15:40:42 2003
@@ -38,7 +38,6 @@
extern int ipv6_route_ioctl(unsigned int cmd, void *arg);
extern int ip6_route_add(struct in6_rtmsg *rtmsg);
-extern int ip6_route_del(struct in6_rtmsg *rtmsg);
extern int ip6_del_rt(struct rt6_info *);
extern int ip6_rt_addr_add(struct in6_addr *addr,
--- ./net/ipv6/Kconfig.~1~ Wed Mar 5 15:32:41 2003
+++ ./net/ipv6/Kconfig Wed Mar 5 15:35:27 2003
@@ -19,6 +19,7 @@
config INET6_AH
tristate "IPv6: AH transformation"
+ depends on IPV6
---help---
Support for IPsec AH.
@@ -26,6 +27,7 @@
config INET6_ESP
tristate "IPv6: ESP transformation"
+ depends on IPV6
---help---
Support for IPsec ESP.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATH] IPv6 IPsec support
2003-03-05 23:25 ` [PATH] " David S. Miller
@ 2003-03-06 0:32 ` Kazunori Miyazawa
2003-03-06 4:43 ` David S. Miller
0 siblings, 1 reply; 10+ messages in thread
From: Kazunori Miyazawa @ 2003-03-06 0:32 UTC (permalink / raw)
To: David S. Miller; +Cc: kuznet, linux-kernel, netdev, usagi-core
Hello David,
On Wed, 05 Mar 2003 15:25:30 -0800 (PST)
"David S. Miller" <davem@redhat.com> wrote:
> From: Kazunori Miyazawa <kazunori@miyazawa.org>
> Date: Wed, 5 Mar 2003 23:30:25 +0900
>
> Hello Miyazawa-san,
>
> I submit the patch to let the kernel support ipv6 ipsec again.
> It is able to comple ipv6 as module.
>
> As promised I applied the patch. I will push it to Linus later
> this evening, or tomorrow.
>
> In this initial checkin I made only 2 minor fixes, they
> are attached below:
>
Thank you very much.
My patch is the first step.
I think there are these TODOs around IPv6 IPsec as far as I remember.
- Extension Header Processing on inbound:
As a result of IPv6 IPsec support, Extension Header processing is devided
into ipv6_parse_exthdrs and ipproto->handler. I think it is better to merge
other Extension Header handling into ipproto->handler.
- Fragmentation support on outbound:
We should change ipv6_build_xmit like ip_append_data style to support
fragmentation with IPsec.
- Removing duplicate codes, clean up and improveing performance.
- Considering relation of IPv6 IPsec and Mobile IPv6. This is future stuff.
Best regards,
--Kazunori Miyazawa (Yokogawa Electric Corporation)
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATH] IPv6 IPsec support
2003-03-06 0:32 ` Kazunori Miyazawa
@ 2003-03-06 4:43 ` David S. Miller
2003-03-18 18:32 ` [PATCH] IPv6 Extension headers (Re: [PATCH] IPv6 IPsec support) Mitsuru KANDA / 神田 充
0 siblings, 1 reply; 10+ messages in thread
From: David S. Miller @ 2003-03-06 4:43 UTC (permalink / raw)
To: kazunori; +Cc: kuznet, linux-kernel, netdev, usagi-core
From: Kazunori Miyazawa <kazunori@miyazawa.org>
Date: Thu, 6 Mar 2003 09:32:19 +0900
- Extension Header Processing on inbound:
As a result of IPv6 IPsec support, Extension Header processing is devided
into ipv6_parse_exthdrs and ipproto->handler. I think it is better to merge
other Extension Header handling into ipproto->handler.
Ok.
- Fragmentation support on outbound:
We should change ipv6_build_xmit like ip_append_data style to support
fragmentation with IPsec.
Please work together with Alexey on this. There are known
major problems on ipv4 side, and it must be resolved before
ipv6 side may be done.
For example, right now a non-TCP packet can do the following. If it
is just slightly smaller than MTU, and when encapsulated in ESP/AH it
becomes larger than MTU, we will not fragment it and too-large frame
will be sent to device.
In my last round of talks with Alexey I believe we were very close to
a possible solution to this problem. The idea was to have a "local
dont-fragment" flag, and at the very last stage of IP output we check
this and either 1) clear DF and fragment or 2) drop packet and send
ICMP message back.
Alexey, what is the current state?
- Removing duplicate codes, clean up and improveing performance.
- Considering relation of IPv6 IPsec and Mobile IPv6. This is future stuff.
Ok.
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] IPv6 Extension headers (Re: [PATCH] IPv6 IPsec support)
2003-03-06 4:43 ` David S. Miller
@ 2003-03-18 18:32 ` Mitsuru KANDA / 神田 充
2003-03-24 5:29 ` [PATCH] IPv6 Extension headers David S. Miller
0 siblings, 1 reply; 10+ messages in thread
From: Mitsuru KANDA / 神田 充 @ 2003-03-18 18:32 UTC (permalink / raw)
To: davem, kuznet; +Cc: linux-kernel, netdev, usagi-core
Hello,
At Wed, 05 Mar 2003 20:43:48 -0800 (PST),
"David S. Miller" <davem@redhat.com> wrote:
>
> From: Kazunori Miyazawa <kazunori@miyazawa.org>
> Date: Thu, 6 Mar 2003 09:32:19 +0900
>
> - Extension Header Processing on inbound:
> As a result of IPv6 IPsec support, Extension Header processing is devided
> into ipv6_parse_exthdrs and ipproto->handler. I think it is better to merge
> other Extension Header handling into ipproto->handler.
>
> Ok.
This patch merges inbound IPv6 extension header processing parts into
inet6_protocols{} like a IPv6 AH/ESP headers.
As a result of this patch, I removed destopt parsing part in xfrm6_rcv()
and removed ipv6_parse_exthdrs().
Could you check this patch?
(This patch is against 2.5.65.)
Best Regards,
-mk
Index: include/net/ipv6.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/ipv6.h,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 ipv6.h
--- include/net/ipv6.h 9 Jan 2003 11:14:19 -0000 1.1.1.4
+++ include/net/ipv6.h 18 Mar 2003 05:11:39 -0000
@@ -203,11 +203,7 @@
extern int ip6_call_ra_chain(struct sk_buff *skb, int sel);
-extern int ipv6_reassembly(struct sk_buff **skb, int);
-
extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
-
-extern int ipv6_parse_exthdrs(struct sk_buff **skb, int);
extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
Index: include/net/protocol.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/protocol.h,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 protocol.h
--- include/net/protocol.h 11 Nov 2002 04:08:20 -0000 1.1.1.3
+++ include/net/protocol.h 18 Mar 2003 05:11:39 -0000
@@ -44,7 +44,7 @@
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
struct inet6_protocol
{
- int (*handler)(struct sk_buff *skb);
+ int (*handler)(struct sk_buff **skbp);
void (*err_handler)(struct sk_buff *skb,
struct inet6_skb_parm *opt,
Index: include/net/transp_v6.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/transp_v6.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 transp_v6.h
--- include/net/transp_v6.h 7 Oct 2002 10:22:46 -0000 1.1.1.1
+++ include/net/transp_v6.h 18 Mar 2003 05:11:39 -0000
@@ -15,6 +15,14 @@
struct flowi;
+/* extention headers */
+extern void ipv6_hopopts_init(void);
+extern void ipv6_rthdr_init(void);
+extern void ipv6_frag_init(void);
+extern void ipv6_nodata_init(void);
+extern void ipv6_destopt_init(void);
+
+/* transport protocols */
extern void rawv6_init(void);
extern void udpv6_init(void);
extern void tcpv6_init(void);
Index: include/net/xfrm.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/xfrm.h,v
retrieving revision 1.1.1.8
diff -u -r1.1.1.8 xfrm.h
--- include/net/xfrm.h 13 Mar 2003 17:29:53 -0000 1.1.1.8
+++ include/net/xfrm.h 18 Mar 2003 05:11:39 -0000
@@ -415,7 +415,7 @@
extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
-extern int xfrm6_rcv(struct sk_buff *skb);
+extern int xfrm6_rcv(struct sk_buff **pskb);
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);
Index: net/ipv4/xfrm_input.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv4/xfrm_input.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 xfrm_input.c
--- net/ipv4/xfrm_input.c 13 Mar 2003 17:29:03 -0000 1.1.1.4
+++ net/ipv4/xfrm_input.c 18 Mar 2003 05:11:39 -0000
@@ -311,8 +311,9 @@
return nexthdr;
}
-int xfrm6_rcv(struct sk_buff *skb)
+int xfrm6_rcv(struct sk_buff **pskb)
{
+ struct sk_buff *skb = *pskb;
int err;
u32 spi, seq;
struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
@@ -325,12 +326,8 @@
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;
- }
+ nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
+ hdr_len = sizeof(struct ipv6hdr);
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr)
@@ -378,18 +375,6 @@
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)
Index: net/ipv6/af_inet6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/af_inet6.c,v
retrieving revision 1.1.1.7
diff -u -r1.1.1.7 af_inet6.c
--- net/ipv6/af_inet6.c 25 Feb 2003 05:33:26 -0000 1.1.1.7
+++ net/ipv6/af_inet6.c 18 Mar 2003 05:11:40 -0000
@@ -793,6 +793,13 @@
addrconf_init();
sit_init();
+ /* Init v6 extention headers. */
+ ipv6_hopopts_init();
+ ipv6_rthdr_init();
+ ipv6_frag_init();
+ ipv6_nodata_init();
+ ipv6_destopt_init();
+
/* Init v6 transport protocols. */
udpv6_init();
tcpv6_init();
Index: net/ipv6/exthdrs.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/exthdrs.c,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 exthdrs.c
--- net/ipv6/exthdrs.c 20 Feb 2003 08:34:32 -0000 1.1.1.3
+++ net/ipv6/exthdrs.c 18 Mar 2003 05:11:40 -0000
@@ -18,6 +18,9 @@
/* Changes:
* yoshfuji : ensure not to overrun while parsing
* tlv options.
+ * Mitsuru KANDA @USAGI : Remove ipv6_parse_exthdrs().
+ * : Register inbound extention header
+ * : handlers as inet6_protocol{}.
*/
#include <linux/errno.h>
@@ -44,20 +47,6 @@
#include <asm/uaccess.h>
/*
- * Parsing inbound headers.
- *
- * Parsing function "func" returns offset wrt skb->nh of the place,
- * where next nexthdr value is stored or NULL, if parsing
- * failed. It should also update skb->h tp point at the next header.
- */
-
-struct hdrtype_proc
-{
- int type;
- int (*func) (struct sk_buff **, int offset);
-};
-
-/*
* Parsing tlv encoded headers.
*
* Parsing function "func" returns 1, if parsing succeed
@@ -164,49 +153,77 @@
{-1, NULL}
};
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, int nhoff)
+int ipv6_destopt_rcv(struct sk_buff **skbp)
{
- struct sk_buff *skb=*skb_ptr;
+ struct sk_buff *skb = *skbp;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+ u8 nexthdr = 0;
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))) {
kfree_skb(skb);
- return -1;
+ return 0;
}
+ nexthdr = ((struct ipv6_destopt_hdr *)skb->h.raw)->nexthdr;
+
opt->dst1 = skb->h.raw - skb->nh.raw;
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
skb->h.raw += ((skb->h.raw[1]+1)<<3);
- return opt->dst1;
+ return -nexthdr;
}
+
+ return 0;
+}
- return -1;
+static struct inet6_protocol destopt_protocol =
+{
+ .handler = ipv6_destopt_rcv,
+};
+
+void __init ipv6_destopt_init(void)
+{
+ if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
+ printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
}
/********************************
NONE header. No data in packet.
********************************/
-static int ipv6_nodata(struct sk_buff **skb_ptr, int nhoff)
+int ipv6_nodata_rcv(struct sk_buff **skbp)
{
- kfree_skb(*skb_ptr);
- return -1;
+ struct sk_buff *skb = *skbp;
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct inet6_protocol nodata_protocol =
+{
+ .handler = ipv6_nodata_rcv,
+};
+
+void __init ipv6_nodata_init(void)
+{
+ if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
+ printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
}
/********************************
Routing header.
********************************/
-static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
+int ipv6_rthdr_rcv(struct sk_buff **skbp)
{
- struct sk_buff *skb = *skb_ptr;
+ struct sk_buff *skb = *skbp;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
struct in6_addr *addr;
struct in6_addr daddr;
int addr_type;
int n, i;
+ u8 nexthdr = 0;
struct ipv6_rt_hdr *hdr;
struct rt0_hdr *rthdr;
@@ -215,15 +232,16 @@
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
IP6_INC_STATS_BH(Ip6InHdrErrors);
kfree_skb(skb);
- return -1;
+ return 0;
}
hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+ nexthdr = hdr->nexthdr;
if ((ipv6_addr_type(&skb->nh.ipv6h->daddr)&IPV6_ADDR_MULTICAST) ||
skb->pkt_type != PACKET_HOST) {
kfree_skb(skb);
- return -1;
+ return 0;
}
looped_back:
@@ -232,24 +250,24 @@
skb->h.raw += (hdr->hdrlen + 1) << 3;
opt->dst0 = opt->dst1;
opt->dst1 = 0;
- return (&hdr->nexthdr) - skb->nh.raw;
+ return -nexthdr;
}
if (hdr->type != IPV6_SRCRT_TYPE_0 || (hdr->hdrlen & 0x01)) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, hdr->type != IPV6_SRCRT_TYPE_0 ? 2 : 1);
- return -1;
+ return 0;
}
/*
* This is the routing header forwarding algorithm from
- * RFC 1883, page 17.
+ * RFC 2460, page 16.
*/
n = hdr->hdrlen >> 1;
if (hdr->segments_left > n) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
- return -1;
+ return 0;
}
/* We are about to mangle packet header. Be careful!
@@ -259,8 +277,8 @@
struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
kfree_skb(skb);
if (skb2 == NULL)
- return -1;
- *skb_ptr = skb = skb2;
+ return 0;
+ *skbp = skb = skb2;
opt = (struct inet6_skb_parm *)skb2->cb;
hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
}
@@ -278,7 +296,7 @@
if (addr_type&IPV6_ADDR_MULTICAST) {
kfree_skb(skb);
- return -1;
+ return 0;
}
ipv6_addr_copy(&daddr, addr);
@@ -289,23 +307,34 @@
ip6_route_input(skb);
if (skb->dst->error) {
dst_input(skb);
- return -1;
+ return 0;
}
if (skb->dst->dev->flags&IFF_LOOPBACK) {
if (skb->nh.ipv6h->hop_limit <= 1) {
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
0, skb->dev);
kfree_skb(skb);
- return -1;
+ return 0;
}
skb->nh.ipv6h->hop_limit--;
goto looped_back;
}
dst_input(skb);
- return -1;
+ return 0;
}
+static struct inet6_protocol rthdr_protocol =
+{
+ .handler = ipv6_rthdr_rcv,
+};
+
+void __init ipv6_rthdr_init(void)
+{
+ if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
+ printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
+};
+
/*
This function inverts received rthdr.
NOTE: specs allow to make it automatically only if
@@ -371,97 +400,6 @@
return opt;
}
-/********************************
- AUTH header.
- ********************************/
-
-/*
- rfc1826 said, that if a host does not implement AUTH header
- it MAY ignore it. We use this hole 8)
-
- Actually, now we can implement OSPFv6 without kernel IPsec.
- Authentication for poors may be done in user space with the same success.
-
- Yes, it means, that we allow application to send/receive
- raw authentication header. Apparently, we suppose, that it knows
- what it does and calculates authentication data correctly.
- Certainly, it is possible only for udp and raw sockets, but not for tcp.
-
- AUTH header has 4byte granular length, which kills all the idea
- behind AUTOMATIC 64bit alignment of IPv6. Now we will lose
- cpu ticks, checking that sender did not something stupid
- and opt->hdrlen is even. Shit! --ANK (980730)
- */
-
-static int ipv6_auth_hdr(struct sk_buff **skb_ptr, int nhoff)
-{
- struct sk_buff *skb=*skb_ptr;
- struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
- int len;
-
- if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
- goto fail;
-
- /*
- * RFC2402 2.2 Payload Length
- * The 8-bit field specifies the length of AH in 32-bit words
- * (4-byte units), minus "2".
- * -- Noriaki Takamiya @USAGI Project
- */
- len = (skb->h.raw[1]+2)<<2;
-
- if (len&7)
- goto fail;
-
- if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len))
- goto fail;
-
- opt->auth = skb->h.raw - skb->nh.raw;
- skb->h.raw += len;
- return opt->auth;
-
-fail:
- kfree_skb(skb);
- return -1;
-}
-
-/* This list MUST NOT contain entry for NEXTHDR_HOP.
- It is parsed immediately after packet received
- and if it occurs somewhere in another place we must
- generate error.
- */
-
-static struct hdrtype_proc hdrproc_lst[] = {
- {NEXTHDR_FRAGMENT, ipv6_reassembly},
- {NEXTHDR_ROUTING, ipv6_routing_header},
- {NEXTHDR_DEST, ipv6_dest_opt},
- {NEXTHDR_NONE, ipv6_nodata},
- {NEXTHDR_AUTH, ipv6_auth_hdr},
- /*
- {NEXTHDR_ESP, ipv6_esp_hdr},
- */
- {-1, NULL}
-};
-
-int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff)
-{
- struct hdrtype_proc *hdrt;
- u8 nexthdr = (*skb_in)->nh.raw[nhoff];
-
-restart:
- for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
- if (hdrt->type == nexthdr) {
- if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) {
- nexthdr = (*skb_in)->nh.raw[nhoff];
- goto restart;
- }
- return -1;
- }
- }
- return nhoff;
-}
-
-
/**********************************
Hop-by-hop options.
**********************************/
@@ -530,6 +468,34 @@
if (ip6_parse_tlv(tlvprochopopt_lst, skb))
return sizeof(struct ipv6hdr);
return -1;
+}
+
+/* This is fake. We have already parsed hopopts in ipv6_rcv(). -mk */
+int ipv6_hopopts_rcv(struct sk_buff **skbp)
+{
+ struct sk_buff *skb = *skbp;
+ u8 nexthdr = 0;
+
+ 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))) {
+ kfree_skb(skb);
+ return 0;
+ }
+ nexthdr = ((struct ipv6_hopopt_hdr *)skb->h.raw)->nexthdr;
+ skb->h.raw += (skb->h.raw[1]+1)<<3;
+
+ return -nexthdr;
+}
+
+static struct inet6_protocol hopopts_protocol =
+{
+ .handler = ipv6_hopopts_rcv,
+};
+
+void __init ipv6_hopopts_init(void)
+{
+ if (inet6_add_protocol(&hopopts_protocol, IPPROTO_HOPOPTS) < 0)
+ printk(KERN_ERR "ipv6_hopopts_init: Could not register protocol\n");
}
/*
Index: net/ipv6/icmp.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/icmp.c,v
retrieving revision 1.1.1.7
diff -u -r1.1.1.7 icmp.c
--- net/ipv6/icmp.c 13 Mar 2003 17:29:06 -0000 1.1.1.7
+++ net/ipv6/icmp.c 18 Mar 2003 05:11:40 -0000
@@ -74,7 +74,7 @@
static struct socket *__icmpv6_socket[NR_CPUS];
#define icmpv6_socket __icmpv6_socket[smp_processor_id()]
-static int icmpv6_rcv(struct sk_buff *skb);
+static int icmpv6_rcv(struct sk_buff **pskb);
static struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
@@ -458,8 +458,9 @@
* Handle icmp messages
*/
-static int icmpv6_rcv(struct sk_buff *skb)
+static int icmpv6_rcv(struct sk_buff **pskb)
{
+ struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr;
Index: net/ipv6/ip6_input.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ip6_input.c,v
retrieving revision 1.1.1.6
diff -u -r1.1.1.6 ip6_input.c
--- net/ipv6/ip6_input.c 13 Mar 2003 17:29:06 -0000 1.1.1.6
+++ net/ipv6/ip6_input.c 18 Mar 2003 05:11:40 -0000
@@ -15,6 +15,10 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+/* Changes
+ *
+ * Mitsuru KANDA @USAGI : Remove ipv6_parse_exthdrs().
+ */
#include <linux/errno.h>
#include <linux/types.h>
@@ -127,38 +131,11 @@
struct inet6_protocol *ipprot;
struct sock *raw_sk;
int nhoff;
- int nexthdr;
+ int nexthdr = hdr->nexthdr;
u8 hash;
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
- /*
- * Parse extension headers
- */
-
- nexthdr = hdr->nexthdr;
- nhoff = offsetof(struct ipv6hdr, nexthdr);
-
- /* Skip hop-by-hop options, they are already parsed. */
- if (nexthdr == NEXTHDR_HOP) {
- nhoff = sizeof(struct ipv6hdr);
- nexthdr = skb->h.raw[0];
- skb->h.raw += (skb->h.raw[1]+1)<<3;
- }
-
- /* This check is sort of optimization.
- It would be stupid to detect for optional headers,
- which are missing with probability of 200%
- */
- if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP &&
- nexthdr != NEXTHDR_AUTH && nexthdr != NEXTHDR_ESP) {
- nhoff = ipv6_parse_exthdrs(&skb, nhoff);
- if (nhoff < 0)
- return 0;
- nexthdr = skb->nh.raw[nhoff];
- hdr = skb->nh.ipv6h;
- }
-
if (!pskb_pull(skb, skb->h.raw - skb->data))
goto discard;
@@ -173,7 +150,7 @@
hash = nexthdr & (MAX_INET_PROTOS - 1);
if ((ipprot = inet6_protos[hash]) != NULL) {
- int ret = ipprot->handler(skb);
+ int ret = ipprot->handler(&skb);
if (ret < 0) {
nexthdr = -ret;
goto resubmit;
@@ -182,6 +159,7 @@
} else {
if (!raw_sk) {
IP6_INC_STATS_BH(Ip6InUnknownProtos);
+ nhoff = offsetof(struct ipv6hdr, nexthdr);
icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
} else {
IP6_INC_STATS_BH(Ip6InDelivers);
Index: net/ipv6/reassembly.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/reassembly.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 reassembly.c
--- net/ipv6/reassembly.c 20 Feb 2003 08:34:32 -0000 1.1.1.4
+++ net/ipv6/reassembly.c 18 Mar 2003 05:11:40 -0000
@@ -23,6 +23,7 @@
* Horst von Brand Add missing #include <linux/string.h>
* Alexey Kuznetsov SMP races, threading, cleanup.
* Patrick McHardy LRU queue of frag heads for evictor.
+ * Mitsuru KANDA @USAGI Register inet6_protocol{}.
*/
#include <linux/config.h>
#include <linux/errno.h>
@@ -525,6 +526,7 @@
int remove_fraghdr = 0;
int payload_len;
int nhoff;
+ u8 nexthdr = 0;
fq_kill(fq);
@@ -535,6 +537,8 @@
payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len;
nhoff = head->h.raw - head->nh.raw;
+ nexthdr = ((struct frag_hdr*)head->h.raw)->nexthdr;
+
if (payload_len > 65535) {
payload_len -= 8;
if (payload_len > 65535)
@@ -609,9 +613,13 @@
if (head->ip_summed == CHECKSUM_HW)
head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
+ if (!pskb_pull(head, head->h.raw - head->data)) {
+ goto out_fail;
+ }
+
IP6_INC_STATS_BH(Ip6ReasmOKs);
fq->fragments = NULL;
- return nhoff;
+ return nexthdr;
out_oversize:
if (net_ratelimit())
@@ -622,16 +630,18 @@
printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
out_fail:
IP6_INC_STATS_BH(Ip6ReasmFails);
- return -1;
+ return 0;
}
-int ipv6_reassembly(struct sk_buff **skbp, int nhoff)
+int ipv6_frag_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
struct net_device *dev = skb->dev;
struct frag_hdr *fhdr;
struct frag_queue *fq;
struct ipv6hdr *hdr;
+ int nhoff = skb->h.raw - skb->nh.raw;
+ u8 nexthdr = 0;
hdr = skb->nh.ipv6h;
@@ -640,15 +650,16 @@
/* Jumbo payload inhibits frag. header */
if (hdr->payload_len==0) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
- return -1;
+ goto discard;
}
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
- return -1;
+ goto discard;
}
hdr = skb->nh.ipv6h;
fhdr = (struct frag_hdr *)skb->h.raw;
+ nexthdr = fhdr->nexthdr;
if (!(fhdr->frag_off & htons(0xFFF9))) {
/* It is not a fragmented frame */
@@ -674,10 +685,22 @@
spin_unlock(&fq->lock);
fq_put(fq);
- return ret;
+ return -ret;
}
+discard:
IP6_INC_STATS_BH(Ip6ReasmFails);
kfree_skb(skb);
- return -1;
+ return 0;
+}
+
+static struct inet6_protocol frag_protocol =
+{
+ .handler = ipv6_frag_rcv,
+};
+
+void __init ipv6_frag_init(void)
+{
+ if (inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT) < 0)
+ printk(KERN_ERR "ipv6_frag_init: Could not register protocol\n");
}
Index: net/ipv6/tcp_ipv6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/tcp_ipv6.c,v
retrieving revision 1.1.1.8
diff -u -r1.1.1.8 tcp_ipv6.c
--- net/ipv6/tcp_ipv6.c 13 Mar 2003 17:29:06 -0000 1.1.1.8
+++ net/ipv6/tcp_ipv6.c 18 Mar 2003 05:11:40 -0000
@@ -1591,8 +1591,9 @@
return 0;
}
-static int tcp_v6_rcv(struct sk_buff *skb)
+static int tcp_v6_rcv(struct sk_buff **pskb)
{
+ struct sk_buff *skb = *pskb;
struct tcphdr *th;
struct sock *sk;
int ret;
Index: net/ipv6/udp.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/udp.c,v
retrieving revision 1.1.1.7
diff -u -r1.1.1.7 udp.c
--- net/ipv6/udp.c 13 Mar 2003 17:29:06 -0000 1.1.1.7
+++ net/ipv6/udp.c 18 Mar 2003 05:11:40 -0000
@@ -641,8 +641,9 @@
read_unlock(&udp_hash_lock);
}
-static int udpv6_rcv(struct sk_buff *skb)
+static int udpv6_rcv(struct sk_buff **pskb)
{
+ struct sk_buff *skb = *pskb;
struct sock *sk;
struct udphdr *uh;
struct net_device *dev = skb->dev;
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2003-03-24 5:29 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-05 14:30 [PATH] IPv6 IPsec support Kazunori Miyazawa
2003-03-05 15:21 ` David S. Miller
2003-03-05 15:48 ` (usagi-core 12294) Re: [PATCH] " YOSHIFUJI Hideaki / 吉藤英明
2003-03-05 23:41 ` David S. Miller
[not found] ` <20030306213217.GA6358@f00f.org>
2003-03-06 23:27 ` David S. Miller
2003-03-05 23:25 ` [PATH] " David S. Miller
2003-03-06 0:32 ` Kazunori Miyazawa
2003-03-06 4:43 ` David S. Miller
2003-03-18 18:32 ` [PATCH] IPv6 Extension headers (Re: [PATCH] IPv6 IPsec support) Mitsuru KANDA / 神田 充
2003-03-24 5:29 ` [PATCH] IPv6 Extension headers David S. Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).