diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index d9db5f6..75e6c9a 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -226,6 +226,21 @@ struct sadb_x_sec_ctx { } __attribute__((packed)); /* sizeof(struct sadb_sec_ctx) = 8 */ +/* Provides the packet (or part of it) that triggered the ACQUIRE msg. + * Used by a MIPv6-aware kernel when performing ACQUIRE for protecting + * a Binding Update message. + * XXX Does not follow draft-sugimoto-mip6-pfkey-migrate-03.txt : added + * XXX a copylen field providing the length of the citation in bytes -- arno + */ +struct sadb_x_packet { + uint16_t sadb_x_packet_len; + uint16_t sadb_x_packet_exttype; + uint32_t sadb_x_packet_copylen; +} __attribute__((packed)); +/* sizeof(struct sadb_x_packet) == 8 */ +/* followed by an IP packet header which triggered the SADB_ACQUIRE message */ + + /* Message types */ #define SADB_RESERVED 0 #define SADB_GETSPI 1 @@ -339,7 +354,8 @@ struct sadb_x_sec_ctx { #define SADB_X_EXT_NAT_T_DPORT 22 #define SADB_X_EXT_NAT_T_OA 23 #define SADB_X_EXT_SEC_CTX 24 -#define SADB_EXT_MAX 24 +#define SADB_X_EXT_PACKET 27 +#define SADB_EXT_MAX 27 /* Identity Extension values */ #define SADB_IDENTTYPE_RESERVED 0 diff --git a/include/net/dst.h b/include/net/dst.h index 82270f9..f4a5000 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -272,9 +272,9 @@ static inline int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, } #else extern int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, - struct sock *sk, int flags); + struct sock *sk, int flags, struct msghdr *trig_pkt); extern int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, - struct sock *sk, int flags); + struct sock *sk, int flags, struct msghdr *trig_pkt); #endif #endif diff --git a/include/net/mip6.h b/include/net/mip6.h index 6327261..c955a28 100644 --- a/include/net/mip6.h +++ b/include/net/mip6.h @@ -54,4 +54,46 @@ struct ip6_mh { #define IP6_MH_TYPE_BERROR 7 /* Binding Error */ #define IP6_MH_TYPE_MAX IP6_MH_TYPE_BERROR +struct ip6_mh_binding_update { + struct ip6_mh ip6mhbu_hdr; + __u16 ip6mhbu_seqno; /* Sequence Number */ + __u16 ip6mhbu_flags; /* See below */ + __u16 ip6mhbu_lifetime; /* Time in unit of 4 sec */ + /* Followed by optional Mobility Options */ +} __attribute__ ((packed)); + +#define IP6_MH_BU_ACK 0x8000 /* Request a binding ack */ +#define IP6_MH_BU_HOME 0x4000 /* Home Registration */ +#define IP6_MH_BU_LLOCAL 0x2000 /* Link-local compatibility */ +#define IP6_MH_BU_KEYM 0x1000 /* Key management mobility */ +#define IP6_MH_BU_MAP 0x0800 /* HMIPv6 MAP Registration */ +#define IP6_MH_BU_MR 0x0400 /* NEMO MR Registration */ + +struct ip6_mh_opt_altcoa { + __u8 ip6moa_type; + __u8 ip6moa_len; + struct in6_addr ip6moa_addr; /* Alternate CoA */ +} __attribute__ ((packed)); + +/* + * Mobility Header Message Option Types + */ +#define IP6_MHOPT_PAD1 0x00 /* PAD1 */ +#define IP6_MHOPT_PADN 0x01 /* PADN */ +#define IP6_MHOPT_BREFRESH 0x02 /* Binding Refresh */ +#define IP6_MHOPT_ALTCOA 0x03 /* Alternate COA */ +#define IP6_MHOPT_NONCEID 0x04 /* Nonce Index */ +#define IP6_MHOPT_BAUTH 0x05 /* Binding Auth Data */ +#define IP6_MHOPT_MOB_NET_PRFX 0x06 /* Mobile Network Prefix */ + +/* + * TLV for HAO (used in DestOpt). Defined in RFC 3775 but structure + * is not defined by Advanced API or MIPv6 API --arno + */ +struct destopt_hao { + __u8 ip6hao_type; + __u8 ip6hao_len; + struct in6_addr ip6hao_hoa; +} __attribute__ ((packed)); + #endif diff --git a/include/net/xfrm.h b/include/net/xfrm.h index bc5cd77..8631f64 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -260,7 +260,7 @@ extern void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c extern void km_state_notify(struct xfrm_state *x, struct km_event *c); struct xfrm_tmpl; -extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); +extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol, struct msghdr *trig_pkt); extern void km_state_expired(struct xfrm_state *x, int hard, u32 pid); extern int __xfrm_state_delete(struct xfrm_state *x); @@ -417,7 +417,7 @@ struct xfrm_mgr struct list_head list; char *id; int (*notify)(struct xfrm_state *x, struct km_event *c); - int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir); + int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir, struct msghdr *trig_pkt); struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir); int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c); @@ -940,7 +940,8 @@ extern struct xfrm_state *xfrm_state_alloc(void); extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, - unsigned short family); + unsigned short family, + struct msghdr *trig_pkt); extern int xfrm_state_check_expire(struct xfrm_state *x); extern void xfrm_state_insert(struct xfrm_state *x); extern int xfrm_state_add(struct xfrm_state *x); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 31737cd..876e885 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -156,7 +156,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - err = xfrm_lookup(&dst, &fl, sk, 0); + err = xfrm_lookup(&dst, &fl, sk, 0, NULL); if (err < 0) { sk->sk_err_soft = -err; goto out; @@ -280,7 +280,7 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - err = xfrm_lookup(&dst, &fl, sk, 0); + err = xfrm_lookup(&dst, &fl, sk, 0, NULL); if (err < 0) goto done; } @@ -369,7 +369,7 @@ static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) /* sk = NULL, but it is safe for now. RST socket required. */ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) { - if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) { + if (xfrm_lookup(&skb->dst, &fl, NULL, 0, NULL) >= 0) { ip6_xmit(dccp_v6_ctl_socket->sk, skb, &fl, NULL, 0); DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); @@ -605,7 +605,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) goto out; } @@ -1043,7 +1043,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - err = __xfrm_lookup(&dst, &fl, sk, 1); + err = __xfrm_lookup(&dst, &fl, sk, 1, NULL); if (err < 0) { if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, &fl); diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index a8bf106..571bc8e 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1198,7 +1198,7 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int f err = __dn_route_output_key(pprt, flp, flags); if (err == 0 && flp->proto) { - err = xfrm_lookup(pprt, flp, NULL, 0); + err = xfrm_lookup(pprt, flp, NULL, 0, NULL); } return err; } @@ -1209,7 +1209,7 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); if (err == 0 && fl->proto) { - err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT)); + err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT), NULL); } return err; } diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index b441929..d902d1c 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -60,7 +60,7 @@ int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type) #ifdef CONFIG_XFRM if (!(IPCB(*pskb)->flags & IPSKB_XFRM_TRANSFORMED) && xfrm_decode_session(*pskb, &fl, AF_INET) == 0) - if (xfrm_lookup(&(*pskb)->dst, &fl, (*pskb)->sk, 0)) + if (xfrm_lookup(&(*pskb)->dst, &fl, (*pskb)->sk, 0, NULL)) return -1; #endif @@ -99,7 +99,7 @@ int ip_xfrm_me_harder(struct sk_buff **pskb) dst = ((struct xfrm_dst *)dst)->route; dst_hold(dst); - if (xfrm_lookup(&dst, &fl, (*pskb)->sk, 0) < 0) + if (xfrm_lookup(&dst, &fl, (*pskb)->sk, 0, NULL) < 0) return -1; dst_release((*pskb)->dst); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 29ca63e..688d80c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2673,7 +2673,7 @@ int ip_route_output_flow(struct rtable **rp, struct flowi *flp, struct sock *sk, flp->fl4_src = (*rp)->rt_src; if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; - err = __xfrm_lookup((struct dst_entry **)rp, flp, sk, flags); + err = __xfrm_lookup((struct dst_entry **)rp, flp, sk, flags, NULL); if (err == -EREMOTE) err = ipv4_dst_blackhole(rp, flp, sk); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index eed0937..965613b 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -673,7 +673,7 @@ int inet6_sk_rebuild_header(struct sock *sk) if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) { sk->sk_err_soft = -err; return err; } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index ba1386d..c6628c2 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -177,7 +177,7 @@ ipv4_connected: if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) { + if ((err = __xfrm_lookup(&dst, &fl, sk, 1, NULL)) < 0) { if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, &fl); if (err < 0) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 4765a29..1ad2f96 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -416,7 +416,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, goto out_dst_release; } - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) goto out; if (ipv6_addr_is_multicast(&fl.fl6_dst)) @@ -515,7 +515,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) goto out; if (ipv6_addr_is_multicast(&fl.fl6_dst)) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 116f94a..47e9354 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -180,7 +180,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok) if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) { sk->sk_route_caps = 0; kfree_skb(skb); return err; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index a0902fb..ff831f5 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -849,7 +849,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, else { dst = ip6_route_output(NULL, fl); - if (dst->error || xfrm_lookup(&dst, fl, NULL, 0) < 0) + if (dst->error || xfrm_lookup(&dst, fl, NULL, 0, NULL) < 0) goto tx_err_link_failure; } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 0358e60..61fd244 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -450,7 +450,7 @@ static void __ndisc_send(struct net_device *dev, if (!dst) return; - err = xfrm_lookup(&dst, &fl, NULL, 0); + err = xfrm_lookup(&dst, &fl, NULL, 0, NULL); if (err < 0) return; @@ -1357,7 +1357,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, if (dst == NULL) return; - err = xfrm_lookup(&dst, &fl, NULL, 0); + err = xfrm_lookup(&dst, &fl, NULL, 0, NULL); if (err) return; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 38b1496..b3e4771 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -27,7 +27,7 @@ int ip6_route_me_harder(struct sk_buff *skb) #ifdef CONFIG_XFRM if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && xfrm_decode_session(skb, &fl, AF_INET6) == 0) - if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0)) + if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0, NULL)) return -1; #endif diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index cb3d241..07182cd 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -101,7 +101,7 @@ static void send_reset(struct sk_buff *oldskb) dst = ip6_route_output(NULL, &fl); if (dst == NULL) return; - if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0)) + if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0, NULL)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index aac6aeb..34c9992 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -842,7 +842,11 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); +#ifdef CONFIG_SADB_X_EXT_PACKET + if ((err = __xfrm_lookup(&dst, &fl, sk, 1, msg)) < 0) { +#else if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) { +#endif if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, &fl); if (err < 0) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 193d9d6..46dff42 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -265,7 +265,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) { + if ((err = __xfrm_lookup(&dst, &fl, sk, 1, NULL)) < 0) { if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, &fl); if (err < 0) @@ -393,7 +393,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) { sk->sk_err_soft = -err; goto out; } @@ -507,7 +507,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, goto done; if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((err = xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) goto done; } @@ -1073,7 +1073,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) /* sk = NULL, but it is safe for now. RST socket required. */ if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) { - if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) { + if (xfrm_lookup(&buff->dst, &fl, NULL, 0, NULL) >= 0) { ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(TCP_MIB_OUTRSTS); @@ -1172,7 +1172,7 @@ static void tcp_v6_send_ack(struct tcp_timewait_sock *tw, security_skb_classify_flow(skb, &fl); if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) { - if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) { + if (xfrm_lookup(&buff->dst, &fl, NULL, 0, NULL) >= 0) { ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); return; @@ -1425,7 +1425,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((xfrm_lookup(&dst, &fl, sk, 0, NULL)) < 0) goto out; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4210951..5cd5459 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -748,7 +748,7 @@ do_udp_sendmsg: if (final_p) ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) { + if ((err = __xfrm_lookup(&dst, &fl, sk, 1, NULL)) < 0) { if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, &fl); if (err < 0) diff --git a/net/key/af_key.c b/net/key/af_key.c index 0f8304b..c3e9c9c 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -31,6 +31,10 @@ #include +#ifdef CONFIG_SADB_X_EXT_PACKET +#include +#endif + #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) @@ -337,6 +341,9 @@ static u8 sadb_ext_min_len[] = { [SADB_X_EXT_NAT_T_DPORT] = (u8) sizeof(struct sadb_x_nat_t_port), [SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address), [SADB_X_EXT_SEC_CTX] = (u8) sizeof(struct sadb_x_sec_ctx), +#ifdef CONFIG_SADB_X_EXT_PACKET + [SADB_X_EXT_PACKET] = (u8) sizeof(struct sadb_x_packet), +#endif }; /* Verify sadb_address_{len,prefixlen} against sa_family. */ @@ -2990,7 +2997,176 @@ static u32 get_acqseq(void) return res; } -static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir) +#ifdef CONFIG_SADB_X_EXT_PACKET + +/* Parse msghdr, looking for a Binding Update with H flag set and an + * Alternate CoA option in the message: + * + * - If found, the length of the sadb_x_packet extension that will + * result from a call to add_sadb_x_packet_from_msghdr() is + * returned (extension followed by the BU and options, with + * reconstructed IPv6 header and Destination Option Header). + * - If not found, 0 is returned. + */ +static int sadb_x_packet_size_from_msghdr(struct msghdr *msg) +{ + int i, found = 0, mh_len, size = 0; + struct ip6_mh_opt_altcoa *cur; + struct iovec *iov; + struct ip6_mh_binding_update *ip6mhbu_msg; + struct sockaddr_in6 *daddr; + + if (!msg) + return 0; + + iov = msg->msg_iov; + if(!(iov && iov[0].iov_base)) + return 0; + + daddr = (struct sockaddr_in6 *)msg->msg_name; + if (!(msg->msg_namelen == sizeof(*daddr) && + daddr && + daddr->sin6_family == AF_INET6 && + daddr->sin6_port == htons(NEXTHDR_MOBILITY))) + return 0; + + /* XXX Validate msg.cmsg_level, cmsg_type and cmsg_len ? --arno */ + + if (iov[0].iov_len < sizeof(struct ip6_mh_binding_update)) + return 0; + + ip6mhbu_msg = (struct ip6_mh_binding_update *)iov[0].iov_base; + if (!(ip6mhbu_msg && + (ip6mhbu_msg->ip6mhbu_hdr.ip6mh_type == IP6_MH_TYPE_BU) && + (ntohs(ip6mhbu_msg->ip6mhbu_flags) & (IP6_MH_BU_HOME)))) + return 0; + +#ifdef CONFIG_SADB_X_EXT_PACKET_DEBUG + printk("SADB_X_EXT_PACKET_DEBUG: " + "Found a triggering BU message.\n"); +#endif + + /* Look for Alternate CoA Mobility Option in iov */ + for (i = 1; i < msg->msg_iovlen ; i++) { + cur = (struct ip6_mh_opt_altcoa *)iov[i].iov_base; + if ((iov[i].iov_len == sizeof(struct ip6_mh_opt_altcoa)) && + (cur && (cur->ip6moa_type == IP6_MHOPT_ALTCOA))) { + found = 1; + break; + } + } + + if (!found) { +#ifdef CONFIG_SADB_X_EXT_PACKET_DEBUG + printk("SADB_X_EXT_PACKET_DEBUG: BU does not " + "contain AltCoA option. Not considering.\n"); +#endif + return 0; + } + + /* Compute size of SADB_X_EXT_PACKET extension including + simplified raw packet */ + mh_len = (ip6mhbu_msg->ip6mhbu_hdr.ip6mh_hdrlen + 1) << 3; + size = sizeof(struct sadb_x_packet) + sizeof(struct ipv6hdr) + + + mh_len; + size = PFKEY_ALIGN8(size); + +#ifdef CONFIG_SADB_X_EXT_PACKET_DEBUG + printk("SADB_X_EXT_PACKET_DEBUG: sadb_x_packet " + "extension will have size %d.\n", size); +#endif + + return size; +} + +/* Construct the sadb_x_packet extension containing the Binding Update packet + * based on stub found in msg. IPv6 header is faked. As their is no specific + * use to pass Destination Option Header with the HAO, MH is directly stacked + * over IPv6 layer. (Userland will use content of AltCoA option to get the + * CoA, not the content of HAO) + * + * Built extension is added at the end of skb. It is expected that enough room + * has been allocated in skb to hold the extension (additional size been + * provided by a previous call to sadb_x_packet_size_from_msghdr()) + * + * -1 is returned on error. 0 otherwise (including NULL msg case). + */ +static int add_sadb_x_packet_from_msghdr(struct sk_buff *skb, struct msghdr *msg) +{ + struct sadb_x_packet * ext; + struct ip6_mh_binding_update *ip6mhbu_msg; + struct sockaddr_in6* daddr; + struct in6_pktinfo *pinfo; +#ifdef CONFIG_SADB_X_EXT_PACKET_DEBUG + struct ip6_mh_opt_altcoa *altcoa = NULL; +#endif + struct ipv6hdr *ipv6h; + struct iovec *iov; + uint16_t pktlen, extlen, mh_len; + int i; + + if (!msg) + return 0; + + /* Checks have been made in sadb_x_packet_size_from_msghdr() */ + iov = msg->msg_iov; + ip6mhbu_msg = (struct ip6_mh_binding_update *)iov[0].iov_base; + mh_len = (ip6mhbu_msg->ip6mhbu_hdr.ip6mh_hdrlen + 1) << 3; + pktlen = sizeof(struct ipv6hdr) + mh_len; + extlen = PFKEY_ALIGN8(sizeof(struct sadb_x_packet) + pktlen); + + ext = (struct sadb_x_packet *)skb_put(skb, sizeof(*ext)); + ext->sadb_x_packet_len = (extlen / sizeof(uint64_t)); + ext->sadb_x_packet_exttype = SADB_X_EXT_PACKET; + ext->sadb_x_packet_copylen = pktlen; + + /* Construct IPv6 packet */ + daddr = (struct sockaddr_in6 *)msg->msg_name; + pinfo = (struct in6_pktinfo *)CMSG_DATA(msg->msg_control); + ipv6h = (struct ipv6hdr *)skb_put(skb,sizeof(struct ipv6hdr)); + ipv6h->version = 0x6; + ipv6h->priority = 0x0; + ipv6h->payload_len = htons(pktlen - sizeof(struct ipv6hdr)); + ipv6h->nexthdr = NEXTHDR_MOBILITY; /* 135 */ + ipv6h->hop_limit = 64; + ipv6_addr_copy(&ipv6h->daddr, &daddr->sin6_addr); /* HA @ */ + ipv6_addr_copy(&ipv6h->saddr, &pinfo->ipi6_addr); /* HoA */ + + /* Finally stack MH BU and options, including the Alt CoA */ + ip6mhbu_msg = (struct ip6_mh_binding_update*)skb_put(skb, mh_len); + if (memcpy_fromiovecend((unsigned char *)ip6mhbu_msg, iov, 0, mh_len)) + return -1; + +#ifdef CONFIG_SADB_X_EXT_PACKET_DEBUG +#define ADDR6TOSTR(x) \ + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:" \ + "%02X%02X:%02X%02X:%02X%02X:%02X%02X\n", \ + x.s6_addr[0], x.s6_addr[1], x.s6_addr[2], x.s6_addr[3], \ + x.s6_addr[4], x.s6_addr[5], x.s6_addr[6], x.s6_addr[7], \ + x.s6_addr[8], x.s6_addr[9], x.s6_addr[10], x.s6_addr[11], \ + x.s6_addr[12], x.s6_addr[13], x.s6_addr[14], x.s6_addr[15] + + for (i = 1; i < msg->msg_iovlen ; i++) { + altcoa = (struct ip6_mh_opt_altcoa *)iov[i].iov_base; + if (altcoa->ip6moa_type == IP6_MHOPT_ALTCOA) { + break; + } + } + + printk("SADB_X_EXT_PACKET_DEBUG: " + "BU created successfully with:\n"); + printk("SADB_X_EXT_PACKET_DEBUG: " + "src address " ADDR6TOSTR(ipv6h->saddr)); + printk("SADB_X_EXT_PACKET_DEBUG: " + "dst address " ADDR6TOSTR(ipv6h->daddr)); + printk("SADB_X_EXT_PACKET_DEBUG: " + "Alt CoA " ADDR6TOSTR(altcoa->ip6moa_addr)); +#endif + return 0; +} +#endif + +static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir, struct msghdr *trig_pkt) { struct sk_buff *skb; struct sadb_msg *hdr; @@ -3005,6 +3181,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct struct sadb_x_sec_ctx *sec_ctx; struct xfrm_sec_ctx *xfrm_ctx; int ctx_size = 0; +#ifdef CONFIG_SADB_X_EXT_PACKET + int sadb_x_packet_size = sadb_x_packet_size_from_msghdr(trig_pkt); +#endif sockaddr_size = pfkey_sockaddr_size(x->props.family); if (!sockaddr_size) @@ -3025,6 +3204,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct size += sizeof(struct sadb_x_sec_ctx) + ctx_size; } +#ifdef CONFIG_SADB_X_EXT_PACKET + size += sadb_x_packet_size ; /* add 0 if no BU in msg */ +#endif skb = alloc_skb(size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; @@ -3134,6 +3316,10 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_ctx->ctx_len); } +#ifdef CONFIG_SADB_X_EXT_PACKET + if (sadb_x_packet_size) + add_sadb_x_packet_from_msghdr(skb, trig_pkt); +#endif return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL); } diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig index b4fb2e4..625a169 100644 --- a/net/xfrm/Kconfig +++ b/net/xfrm/Kconfig @@ -71,3 +71,24 @@ config NET_KEY_MIGRATE If unsure, say N. +config SADB_X_EXT_PACKET + bool "SADB_X_EXT_PACKET support (EXPERIMENTAL)" + depends on NET_KEY && XFRM && EXPERIMENTAL + ---help--- + Support for SADB_X_PACKET extension used for passing triggering + packet in the ACQUIRE message sent to registered key managers. + One use of the extension is in bootstrapping in Mobile IPv6 + environments where IPsec is used with dynamic keying (IKE) for + traffic protection. + Information can be found in + + If unsure, say N. + +config SADB_X_EXT_PACKET_DEBUG + bool "SADB_X_EXT_PACKET debug messages (EXPERIMENTAL)" + depends on NET_KEY && SADB_X_EXT_PACKET && XFRM && EXPERIMENTAL + ---help--- + Provides debug messages associated with SADB_X_EXT_PACKET + extension operations. + + If unsure, say N. \ No newline at end of file diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 53f0c90..015400b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1287,7 +1287,7 @@ xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote, static int xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl, struct xfrm_state **xfrm, - unsigned short family) + unsigned short family, struct msghdr *trig_pkt) { int nx; int i, error; @@ -1313,7 +1313,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl, } } - x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family); + x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family, trig_pkt); if (x && x->km.state == XFRM_STATE_VALID) { xfrm[nx++] = x; @@ -1341,7 +1341,7 @@ fail: static int xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl, struct xfrm_state **xfrm, - unsigned short family) + unsigned short family, struct msghdr *trig_pkt) { struct xfrm_state *tp[XFRM_MAX_DEPTH]; struct xfrm_state **tpp = (npols > 1) ? tp : xfrm; @@ -1356,7 +1356,7 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl, goto fail; } - ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family); + ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family, trig_pkt); if (ret < 0) { error = ret; goto fail; @@ -1454,7 +1454,7 @@ static int stale_bundle(struct dst_entry *dst); * on interfaces with disabled IPsec. */ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, - struct sock *sk, int flags) + struct sock *sk, int flags, struct msghdr *trig_pkt) { struct xfrm_policy *policy; struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; @@ -1576,7 +1576,7 @@ restart: } #endif - nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family); + nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family, trig_pkt); if (unlikely(nx<0)) { err = nx; @@ -1596,7 +1596,7 @@ restart: set_current_state(TASK_RUNNING); remove_wait_queue(&km_waitq, &wait); - nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family); + nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family, trig_pkt); if (nx == -EAGAIN && signal_pending(current)) { XFRM_INC_STATS(XFRM_MIB_OUTNOSTATES); @@ -1685,9 +1685,9 @@ error: EXPORT_SYMBOL(__xfrm_lookup); int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, - struct sock *sk, int flags) + struct sock *sk, int flags, struct msghdr *trig_pkt) { - int err = __xfrm_lookup(dst_p, fl, sk, flags); + int err = __xfrm_lookup(dst_p, fl, sk, flags, trig_pkt); if (err == -EREMOTE) { dst_release(*dst_p); @@ -1960,7 +1960,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) return 0; } - return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0; + return xfrm_lookup(&skb->dst, &fl, NULL, 0, NULL) == 0; } EXPORT_SYMBOL(__xfrm_route_forward); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 91b3d3a..08c3a57 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -186,7 +186,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock); int __xfrm_state_delete(struct xfrm_state *x); -int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); +int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol, struct msghdr *trig_pkt); void km_state_expired(struct xfrm_state *x, int hard, u32 pid); static void xfrm_state_gc_destroy(struct xfrm_state *x) @@ -579,7 +579,7 @@ struct xfrm_state * xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, - unsigned short family) + unsigned short family, struct msghdr *trig_pkt) { unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); struct hlist_node *entry; @@ -656,7 +656,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, goto out; } - if (km_query(x, tmpl, pol) == 0) { + if (km_query(x, tmpl, pol, trig_pkt) == 0) { x->km.state = XFRM_STATE_ACQ; hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(daddr, saddr, family); @@ -1509,14 +1509,15 @@ EXPORT_SYMBOL(km_state_expired); * We send to all registered managers regardless of failure * We are happy with one success */ -int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) +int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, + struct xfrm_policy *pol, struct msghdr *trig_pkt) { int err = -EINVAL, acqret; struct xfrm_mgr *km; read_lock(&xfrm_km_lock); list_for_each_entry(km, &xfrm_km_list, list) { - acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT); + acqret = km->acquire(x, t, pol, XFRM_POLICY_OUT, trig_pkt); if (!acqret) err = acqret; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index c06883b..dfacc39 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1733,7 +1733,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, t->aalgos = ua->aalgos; t->ealgos = ua->ealgos; t->calgos = ua->calgos; - err = km_query(x, t, xp); + err = km_query(x, t, xp, NULL); } @@ -2256,7 +2256,8 @@ nlmsg_failure: } static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, - struct xfrm_policy *xp, int dir) + struct xfrm_policy *xp, int dir, + struct msghdr *trig_pkt) { struct sk_buff *skb; size_t len;