Commit fb92f57b authored by David S. Miller's avatar David S. Miller

Merge branch 'icmp6-support-rfc-4884'

Willem de Bruijn says:

====================
icmp6: support rfc 4884

Extend the feature merged earlier this week for IPv4 to IPv6.

I expected this to be a single patch, but patch 1 seemed better to be
stand-alone

patch 1: small fix in length calculation
patch 2: factor out ipv4-specific
patch 3: add ipv6

changes v1->v2: add missing static keyword in patch 3
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 623b57be 01370434
...@@ -37,6 +37,7 @@ static inline bool icmp_is_err(int type) ...@@ -37,6 +37,7 @@ static inline bool icmp_is_err(int type)
} }
void ip_icmp_error_rfc4884(const struct sk_buff *skb, void ip_icmp_error_rfc4884(const struct sk_buff *skb,
struct sock_ee_data_rfc4884 *out); struct sock_ee_data_rfc4884 *out,
int thlen, int off);
#endif /* _LINUX_ICMP_H */ #endif /* _LINUX_ICMP_H */
...@@ -283,6 +283,7 @@ struct ipv6_pinfo { ...@@ -283,6 +283,7 @@ struct ipv6_pinfo {
autoflowlabel:1, autoflowlabel:1,
autoflowlabel_set:1, autoflowlabel_set:1,
mc_all:1, mc_all:1,
recverr_rfc4884:1,
rtalert_isolate:1; rtalert_isolate:1;
__u8 min_hopcount; __u8 min_hopcount;
__u8 tclass; __u8 tclass;
......
...@@ -68,6 +68,7 @@ struct icmp6hdr { ...@@ -68,6 +68,7 @@ struct icmp6hdr {
#define icmp6_mtu icmp6_dataun.un_data32[0] #define icmp6_mtu icmp6_dataun.un_data32[0]
#define icmp6_unused icmp6_dataun.un_data32[0] #define icmp6_unused icmp6_dataun.un_data32[0]
#define icmp6_maxdelay icmp6_dataun.un_data16[0] #define icmp6_maxdelay icmp6_dataun.un_data16[0]
#define icmp6_datagram_len icmp6_dataun.un_data8[0]
#define icmp6_router icmp6_dataun.u_nd_advt.router #define icmp6_router icmp6_dataun.u_nd_advt.router
#define icmp6_solicited icmp6_dataun.u_nd_advt.solicited #define icmp6_solicited icmp6_dataun.u_nd_advt.solicited
#define icmp6_override icmp6_dataun.u_nd_advt.override #define icmp6_override icmp6_dataun.u_nd_advt.override
......
...@@ -179,6 +179,7 @@ struct in6_flowlabel_req { ...@@ -179,6 +179,7 @@ struct in6_flowlabel_req {
#define IPV6_LEAVE_ANYCAST 28 #define IPV6_LEAVE_ANYCAST 28
#define IPV6_MULTICAST_ALL 29 #define IPV6_MULTICAST_ALL 29
#define IPV6_ROUTER_ALERT_ISOLATE 30 #define IPV6_ROUTER_ALERT_ISOLATE 30
#define IPV6_RECVERR_RFC4884 31
/* IPV6_MTU_DISCOVER values */ /* IPV6_MTU_DISCOVER values */
#define IPV6_PMTUDISC_DONT 0 #define IPV6_PMTUDISC_DONT 0
......
...@@ -1151,29 +1151,16 @@ static bool ip_icmp_error_rfc4884_validate(const struct sk_buff *skb, int off) ...@@ -1151,29 +1151,16 @@ static bool ip_icmp_error_rfc4884_validate(const struct sk_buff *skb, int off)
} }
void ip_icmp_error_rfc4884(const struct sk_buff *skb, void ip_icmp_error_rfc4884(const struct sk_buff *skb,
struct sock_ee_data_rfc4884 *out) struct sock_ee_data_rfc4884 *out,
int thlen, int off)
{ {
int hlen, off; int hlen;
switch (icmp_hdr(skb)->type) { /* original datagram headers: end of icmph to payload (skb->data) */
case ICMP_DEST_UNREACH: hlen = -skb_transport_offset(skb) - thlen;
case ICMP_TIME_EXCEEDED:
case ICMP_PARAMETERPROB:
break;
default:
return;
}
/* outer headers up to inner iph. skb->data is at inner payload */
hlen = -skb_transport_offset(skb) - sizeof(struct icmphdr);
/* per rfc 791: maximum packet length of 576 bytes */
if (hlen + skb->len > 576)
return;
/* per rfc 4884: minimal datagram length of 128 bytes */ /* per rfc 4884: minimal datagram length of 128 bytes */
off = icmp_hdr(skb)->un.reserved[1] * sizeof(u32); if (off < 128 || off < hlen)
if (off < 128)
return; return;
/* kernel has stripped headers: return payload offset in bytes */ /* kernel has stripped headers: return payload offset in bytes */
...@@ -1186,6 +1173,7 @@ void ip_icmp_error_rfc4884(const struct sk_buff *skb, ...@@ -1186,6 +1173,7 @@ void ip_icmp_error_rfc4884(const struct sk_buff *skb,
if (!ip_icmp_error_rfc4884_validate(skb, off)) if (!ip_icmp_error_rfc4884_validate(skb, off))
out->flags |= SO_EE_RFC4884_FLAG_INVALID; out->flags |= SO_EE_RFC4884_FLAG_INVALID;
} }
EXPORT_SYMBOL_GPL(ip_icmp_error_rfc4884);
int icmp_err(struct sk_buff *skb, u32 info) int icmp_err(struct sk_buff *skb, u32 info)
{ {
......
...@@ -390,6 +390,18 @@ int ip_ra_control(struct sock *sk, unsigned char on, ...@@ -390,6 +390,18 @@ int ip_ra_control(struct sock *sk, unsigned char on,
return 0; return 0;
} }
static void ipv4_icmp_error_rfc4884(const struct sk_buff *skb,
struct sock_ee_data_rfc4884 *out)
{
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
case ICMP_TIME_EXCEEDED:
case ICMP_PARAMETERPROB:
ip_icmp_error_rfc4884(skb, out, sizeof(struct icmphdr),
icmp_hdr(skb)->un.reserved[1] * 4);
}
}
void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload) __be16 port, u32 info, u8 *payload)
{ {
...@@ -413,7 +425,7 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, ...@@ -413,7 +425,7 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
if (skb_pull(skb, payload - skb->data)) { if (skb_pull(skb, payload - skb->data)) {
if (inet_sk(sk)->recverr_rfc4884) if (inet_sk(sk)->recverr_rfc4884)
ip_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884); ipv4_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
if (sock_queue_err_skb(sk, skb) == 0) if (sock_queue_err_skb(sk, skb) == 0)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/route.h> #include <linux/route.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/icmp.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ndisc.h> #include <net/ndisc.h>
...@@ -284,6 +285,17 @@ int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr, ...@@ -284,6 +285,17 @@ int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr,
} }
EXPORT_SYMBOL_GPL(ip6_datagram_connect_v6_only); EXPORT_SYMBOL_GPL(ip6_datagram_connect_v6_only);
static void ipv6_icmp_error_rfc4884(const struct sk_buff *skb,
struct sock_ee_data_rfc4884 *out)
{
switch (icmp6_hdr(skb)->icmp6_type) {
case ICMPV6_TIME_EXCEED:
case ICMPV6_DEST_UNREACH:
ip_icmp_error_rfc4884(skb, out, sizeof(struct icmp6hdr),
icmp6_hdr(skb)->icmp6_datagram_len * 8);
}
}
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload) __be16 port, u32 info, u8 *payload)
{ {
...@@ -313,6 +325,10 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, ...@@ -313,6 +325,10 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
serr->port = port; serr->port = port;
__skb_pull(skb, payload - skb->data); __skb_pull(skb, payload - skb->data);
if (inet6_sk(sk)->recverr_rfc4884)
ipv6_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
if (sock_queue_err_skb(sk, skb)) if (sock_queue_err_skb(sk, skb))
......
...@@ -965,6 +965,14 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -965,6 +965,14 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
np->rxopt.bits.recvfragsize = valbool; np->rxopt.bits.recvfragsize = valbool;
retv = 0; retv = 0;
break; break;
case IPV6_RECVERR_RFC4884:
if (optlen < sizeof(int))
goto e_inval;
if (val < 0 || val > 1)
goto e_inval;
np->recverr_rfc4884 = valbool;
retv = 0;
break;
} }
release_sock(sk); release_sock(sk);
...@@ -1439,6 +1447,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -1439,6 +1447,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
val = np->rtalert_isolate; val = np->rtalert_isolate;
break; break;
case IPV6_RECVERR_RFC4884:
val = np->recverr_rfc4884;
break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment