Skip to content

Commit

Permalink
net: Convert protocol error handlers from void to int
Browse files Browse the repository at this point in the history
We'll need this to handle ICMP errors for tunnels without a sending socket
(i.e. FoU and GUE). There, we might have to look up different types of IP
tunnels, registered as network protocols, before we get a match, so we
want this for the error handlers of IPPROTO_IPIP and IPPROTO_IPV6 in both
inet_protos and inet6_protos. These error codes will be used in the next
patch.

For consistency, return sensible error codes in protocol error handlers
whenever handlers can't handle errors because, even if valid, they don't
match a protocol or any of its states.

This has no effect on existing error handling paths.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Stefano Brivio authored and David S. Miller committed Nov 9, 2018
1 parent ce73366 commit 32bbd87
Show file tree
Hide file tree
Showing 27 changed files with 177 additions and 121 deletions.
2 changes: 1 addition & 1 deletion include/net/icmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct net;

void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
int icmp_rcv(struct sk_buff *skb);
void icmp_err(struct sk_buff *skb, u32 info);
int icmp_err(struct sk_buff *skb, u32 info);
int icmp_init(void);
void icmp_out_count(struct net *net, unsigned char type);

Expand Down
9 changes: 7 additions & 2 deletions include/net/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ struct net_protocol {
int (*early_demux)(struct sk_buff *skb);
int (*early_demux_handler)(struct sk_buff *skb);
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);

/* This returns an error if we weren't able to handle the error. */
int (*err_handler)(struct sk_buff *skb, u32 info);

unsigned int no_policy:1,
netns_ok:1,
/* does the protocol do more stringent
Expand All @@ -58,10 +61,12 @@ struct inet6_protocol {
void (*early_demux_handler)(struct sk_buff *skb);
int (*handler)(struct sk_buff *skb);

void (*err_handler)(struct sk_buff *skb,
/* This returns an error if we weren't able to handle the error. */
int (*err_handler)(struct sk_buff *skb,
struct inet6_skb_parm *opt,
u8 type, u8 code, int offset,
__be32 info);

unsigned int flags; /* INET6_PROTO_xxx */
};

Expand Down
2 changes: 1 addition & 1 deletion include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ int sctp_primitive_RECONF(struct net *net, struct sctp_association *asoc,
* sctp/input.c
*/
int sctp_rcv(struct sk_buff *skb);
void sctp_v4_err(struct sk_buff *skb, u32 info);
int sctp_v4_err(struct sk_buff *skb, u32 info);
void sctp_hash_endpoint(struct sctp_endpoint *);
void sctp_unhash_endpoint(struct sctp_endpoint *);
struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
Expand Down
2 changes: 1 addition & 1 deletion include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ extern struct proto tcp_prot;

void tcp_tasklet_init(void);

void tcp_v4_err(struct sk_buff *skb, u32);
int tcp_v4_err(struct sk_buff *skb, u32);

void tcp_shutdown(struct sock *sk, int how);

Expand Down
2 changes: 1 addition & 1 deletion include/net/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
const struct sock *));
void udp_err(struct sk_buff *, u32);
int udp_err(struct sk_buff *, u32);
int udp_abort(struct sock *sk, int err);
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
int udp_push_pending_frames(struct sock *sk);
Expand Down
13 changes: 8 additions & 5 deletions net/dccp/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ EXPORT_SYMBOL(dccp_req_err);
* check at all. A more general error queue to queue errors for later handling
* is probably better.
*/
static void dccp_v4_err(struct sk_buff *skb, u32 info)
static int dccp_v4_err(struct sk_buff *skb, u32 info)
{
const struct iphdr *iph = (struct iphdr *)skb->data;
const u8 offset = iph->ihl << 2;
Expand Down Expand Up @@ -259,16 +259,18 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
inet_iif(skb), 0);
if (!sk) {
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
return;
return -ENOENT;
}

if (sk->sk_state == DCCP_TIME_WAIT) {
inet_twsk_put(inet_twsk(sk));
return;
return 0;
}
seq = dccp_hdr_seq(dh);
if (sk->sk_state == DCCP_NEW_SYN_RECV)
return dccp_req_err(sk, seq);
if (sk->sk_state == DCCP_NEW_SYN_RECV) {
dccp_req_err(sk, seq);
return 0;
}

bh_lock_sock(sk);
/* If too many ICMPs get dropped on busy
Expand Down Expand Up @@ -357,6 +359,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
out:
bh_unlock_sock(sk);
sock_put(sk);
return 0;
}

static inline __sum16 dccp_v4_csum_finish(struct sk_buff *skb,
Expand Down
13 changes: 8 additions & 5 deletions net/dccp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)

}

static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;
Expand Down Expand Up @@ -96,16 +96,18 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (!sk) {
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
ICMP6_MIB_INERRORS);
return;
return -ENOENT;
}

if (sk->sk_state == DCCP_TIME_WAIT) {
inet_twsk_put(inet_twsk(sk));
return;
return 0;
}
seq = dccp_hdr_seq(dh);
if (sk->sk_state == DCCP_NEW_SYN_RECV)
return dccp_req_err(sk, seq);
if (sk->sk_state == DCCP_NEW_SYN_RECV) {
dccp_req_err(sk, seq);
return 0;
}

bh_lock_sock(sk);
if (sock_owned_by_user(sk))
Expand Down Expand Up @@ -183,6 +185,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
out:
bh_unlock_sock(sk);
sock_put(sk);
return 0;
}


Expand Down
9 changes: 7 additions & 2 deletions net/ipv4/gre_demux.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,20 +151,25 @@ static int gre_rcv(struct sk_buff *skb)
return NET_RX_DROP;
}

static void gre_err(struct sk_buff *skb, u32 info)
static int gre_err(struct sk_buff *skb, u32 info)
{
const struct gre_protocol *proto;
const struct iphdr *iph = (const struct iphdr *)skb->data;
u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
int err = 0;

if (ver >= GREPROTO_MAX)
return;
return -EINVAL;

rcu_read_lock();
proto = rcu_dereference(gre_proto[ver]);
if (proto && proto->err_handler)
proto->err_handler(skb, info);
else
err = -EPROTONOSUPPORT;
rcu_read_unlock();

return err;
}

static const struct net_protocol net_gre_protocol = {
Expand Down
6 changes: 4 additions & 2 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ int icmp_rcv(struct sk_buff *skb)
goto drop;
}

void icmp_err(struct sk_buff *skb, u32 info)
int icmp_err(struct sk_buff *skb, u32 info)
{
struct iphdr *iph = (struct iphdr *)skb->data;
int offset = iph->ihl<<2;
Expand All @@ -1094,13 +1094,15 @@ void icmp_err(struct sk_buff *skb, u32 info)
*/
if (icmph->type != ICMP_ECHOREPLY) {
ping_err(skb, offset, info);
return;
return 0;
}

if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
ipv4_update_pmtu(skb, net, info, 0, IPPROTO_ICMP);
else if (type == ICMP_REDIRECT)
ipv4_redirect(skb, net, 0, IPPROTO_ICMP);

return 0;
}

/*
Expand Down
48 changes: 25 additions & 23 deletions net/ipv4/ip_gre.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ static unsigned int ipgre_net_id __read_mostly;
static unsigned int gre_tap_net_id __read_mostly;
static unsigned int erspan_net_id __read_mostly;

static void ipgre_err(struct sk_buff *skb, u32 info,
const struct tnl_ptk_info *tpi)
static int ipgre_err(struct sk_buff *skb, u32 info,
const struct tnl_ptk_info *tpi)
{

/* All the routers (except for Linux) return only
Expand All @@ -146,17 +146,32 @@ static void ipgre_err(struct sk_buff *skb, u32 info,
unsigned int data_len = 0;
struct ip_tunnel *t;

if (tpi->proto == htons(ETH_P_TEB))
itn = net_generic(net, gre_tap_net_id);
else if (tpi->proto == htons(ETH_P_ERSPAN) ||
tpi->proto == htons(ETH_P_ERSPAN2))
itn = net_generic(net, erspan_net_id);
else
itn = net_generic(net, ipgre_net_id);

iph = (const struct iphdr *)(icmp_hdr(skb) + 1);
t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
iph->daddr, iph->saddr, tpi->key);

if (!t)
return -ENOENT;

switch (type) {
default:
case ICMP_PARAMETERPROB:
return;
return 0;

case ICMP_DEST_UNREACH:
switch (code) {
case ICMP_SR_FAILED:
case ICMP_PORT_UNREACH:
/* Impossible event. */
return;
return 0;
default:
/* All others are translated to HOST_UNREACH.
rfc2003 contains "deep thoughts" about NET_UNREACH,
Expand All @@ -168,48 +183,35 @@ static void ipgre_err(struct sk_buff *skb, u32 info,

case ICMP_TIME_EXCEEDED:
if (code != ICMP_EXC_TTL)
return;
return 0;
data_len = icmp_hdr(skb)->un.reserved[1] * 4; /* RFC 4884 4.1 */
break;

case ICMP_REDIRECT:
break;
}

if (tpi->proto == htons(ETH_P_TEB))
itn = net_generic(net, gre_tap_net_id);
else if (tpi->proto == htons(ETH_P_ERSPAN) ||
tpi->proto == htons(ETH_P_ERSPAN2))
itn = net_generic(net, erspan_net_id);
else
itn = net_generic(net, ipgre_net_id);

iph = (const struct iphdr *)(icmp_hdr(skb) + 1);
t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
iph->daddr, iph->saddr, tpi->key);

if (!t)
return;

#if IS_ENABLED(CONFIG_IPV6)
if (tpi->proto == htons(ETH_P_IPV6) &&
!ip6_err_gen_icmpv6_unreach(skb, iph->ihl * 4 + tpi->hdr_len,
type, data_len))
return;
return 0;
#endif

if (t->parms.iph.daddr == 0 ||
ipv4_is_multicast(t->parms.iph.daddr))
return;
return 0;

if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
return;
return 0;

if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
t->err_count++;
else
t->err_count = 1;
t->err_time = jiffies;

return 0;
}

static void gre_err(struct sk_buff *skb, u32 info)
Expand Down
14 changes: 7 additions & 7 deletions net/ipv4/ipip.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ static int ipip_err(struct sk_buff *skb, u32 info)
struct ip_tunnel *t;
int err = 0;

t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->daddr, iph->saddr, 0);
if (!t) {
err = -ENOENT;
goto out;
}

switch (type) {
case ICMP_DEST_UNREACH:
switch (code) {
Expand Down Expand Up @@ -167,13 +174,6 @@ static int ipip_err(struct sk_buff *skb, u32 info)
goto out;
}

t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->daddr, iph->saddr, 0);
if (!t) {
err = -ENOENT;
goto out;
}

if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
ipv4_update_pmtu(skb, net, info, t->parms.link, iph->protocol);
goto out;
Expand Down
22 changes: 12 additions & 10 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ EXPORT_SYMBOL(tcp_req_err);
*
*/

void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
int tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
{
const struct iphdr *iph = (const struct iphdr *)icmp_skb->data;
struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2));
Expand All @@ -446,20 +446,21 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
inet_iif(icmp_skb), 0);
if (!sk) {
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
return;
return -ENOENT;
}
if (sk->sk_state == TCP_TIME_WAIT) {
inet_twsk_put(inet_twsk(sk));
return;
return 0;
}
seq = ntohl(th->seq);
if (sk->sk_state == TCP_NEW_SYN_RECV)
return tcp_req_err(sk, seq,
type == ICMP_PARAMETERPROB ||
type == ICMP_TIME_EXCEEDED ||
(type == ICMP_DEST_UNREACH &&
(code == ICMP_NET_UNREACH ||
code == ICMP_HOST_UNREACH)));
if (sk->sk_state == TCP_NEW_SYN_RECV) {
tcp_req_err(sk, seq, type == ICMP_PARAMETERPROB ||
type == ICMP_TIME_EXCEEDED ||
(type == ICMP_DEST_UNREACH &&
(code == ICMP_NET_UNREACH ||
code == ICMP_HOST_UNREACH)));
return 0;
}

bh_lock_sock(sk);
/* If too many ICMPs get dropped on busy
Expand Down Expand Up @@ -613,6 +614,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
out:
bh_unlock_sock(sk);
sock_put(sk);
return 0;
}

void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
Expand Down
Loading

0 comments on commit 32bbd87

Please sign in to comment.