Skip to content

Commit

Permalink
sctp: process sctp over udp icmp err on sctp side
Browse files Browse the repository at this point in the history
Previously, sctp over udp was using udp tunnel's icmp err process, which
only does sk lookup on sctp side. However for sctp's icmp error process,
there are more things to do, like syncing assoc pmtu/retransmit packets
for toobig type err, and starting proto_unreach_timer for unreach type
err etc.

Now after adding PLPMTUD, which also requires to process toobig type err
on sctp side. This patch is to process icmp err on sctp side by parsing
the type/code/info in .encap_err_lookup and call sctp's icmp processing
functions. Note as the 'redirect' err process needs to know the outer
ip(v6) header's, we have to leave it to udp(v6)_err to handle it.

Signed-off-by: Xin Long <[email protected]>
Acked-by: Marcelo Ricardo Leitner <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
lxin authored and davem330 committed Jun 22, 2021
1 parent d830607 commit 9e47df0
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 19 deletions.
2 changes: 2 additions & 0 deletions include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
struct sctphdr *, struct sctp_association **,
struct sctp_transport **);
void sctp_err_finish(struct sock *, struct sctp_transport *);
int sctp_udp_v4_err(struct sock *sk, struct sk_buff *skb);
int sctp_udp_v6_err(struct sock *sk, struct sk_buff *skb);
void sctp_icmp_frag_needed(struct sock *, struct sctp_association *,
struct sctp_transport *t, __u32 pmtu);
void sctp_icmp_redirect(struct sock *, struct sctp_transport *,
Expand Down
30 changes: 30 additions & 0 deletions net/sctp/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,36 @@ int sctp_v4_err(struct sk_buff *skb, __u32 info)
return 0;
}

int sctp_udp_v4_err(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
struct sctp_association *asoc;
struct sctp_transport *t;
struct icmphdr *hdr;
__u32 info = 0;

skb->transport_header += sizeof(struct udphdr);
sk = sctp_err_lookup(net, AF_INET, skb, sctp_hdr(skb), &asoc, &t);
if (!sk) {
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
return -ENOENT;
}

skb->transport_header -= sizeof(struct udphdr);
hdr = (struct icmphdr *)(skb_network_header(skb) - sizeof(struct icmphdr));
if (hdr->type == ICMP_REDIRECT) {
/* can't be handled without outer iphdr known, leave it to udp_err */
sctp_err_finish(sk, t);
return 0;
}
if (hdr->type == ICMP_DEST_UNREACH && hdr->code == ICMP_FRAG_NEEDED)
info = ntohs(hdr->un.frag.mtu);
sctp_v4_err_handle(t, skb, hdr->type, hdr->code, info);

sctp_err_finish(sk, t);
return 1;
}

/*
* RFC 2960, 8.4 - Handle "Out of the blue" Packets.
*
Expand Down
30 changes: 30 additions & 0 deletions net/sctp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,36 @@ static int sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
}

int sctp_udp_v6_err(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
struct sctp_association *asoc;
struct sctp_transport *t;
struct icmp6hdr *hdr;
__u32 info = 0;

skb->transport_header += sizeof(struct udphdr);
sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &t);
if (!sk) {
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
return -ENOENT;
}

skb->transport_header -= sizeof(struct udphdr);
hdr = (struct icmp6hdr *)(skb_network_header(skb) - sizeof(struct icmp6hdr));
if (hdr->icmp6_type == NDISC_REDIRECT) {
/* can't be handled without outer ip6hdr known, leave it to udpv6_err */
sctp_err_finish(sk, t);
return 0;
}
if (hdr->icmp6_type == ICMPV6_PKT_TOOBIG)
info = ntohl(hdr->icmp6_mtu);
sctp_v6_err_handle(t, skb, hdr->icmp6_type, hdr->icmp6_code, info);

sctp_err_finish(sk, t);
return 1;
}

static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *t)
{
struct dst_entry *dst = dst_clone(t->dst);
Expand Down
21 changes: 2 additions & 19 deletions net/sctp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -850,23 +850,6 @@ static int sctp_udp_rcv(struct sock *sk, struct sk_buff *skb)
return 0;
}

static int sctp_udp_err_lookup(struct sock *sk, struct sk_buff *skb)
{
struct sctp_association *asoc;
struct sctp_transport *t;
int family;

skb->transport_header += sizeof(struct udphdr);
family = (ip_hdr(skb)->version == 4) ? AF_INET : AF_INET6;
sk = sctp_err_lookup(dev_net(skb->dev), family, skb, sctp_hdr(skb),
&asoc, &t);
if (!sk)
return -ENOENT;

sctp_err_finish(sk, t);
return 0;
}

int sctp_udp_sock_start(struct net *net)
{
struct udp_tunnel_sock_cfg tuncfg = {NULL};
Expand All @@ -885,7 +868,7 @@ int sctp_udp_sock_start(struct net *net)

tuncfg.encap_type = 1;
tuncfg.encap_rcv = sctp_udp_rcv;
tuncfg.encap_err_lookup = sctp_udp_err_lookup;
tuncfg.encap_err_lookup = sctp_udp_v4_err;
setup_udp_tunnel_sock(net, sock, &tuncfg);
net->sctp.udp4_sock = sock->sk;

Expand All @@ -907,7 +890,7 @@ int sctp_udp_sock_start(struct net *net)

tuncfg.encap_type = 1;
tuncfg.encap_rcv = sctp_udp_rcv;
tuncfg.encap_err_lookup = sctp_udp_err_lookup;
tuncfg.encap_err_lookup = sctp_udp_v6_err;
setup_udp_tunnel_sock(net, sock, &tuncfg);
net->sctp.udp6_sock = sock->sk;
#endif
Expand Down

0 comments on commit 9e47df0

Please sign in to comment.