Add support for handling ICMP and ICMP6 messages sent in response
to SCTP/UDP/IP and SCTP/UDP/IPv6 packets.
This commit is contained in:
parent
7ae2ff0dba
commit
fd7af143e2
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=298800
@ -140,9 +140,10 @@ struct icmp {
|
||||
/*
|
||||
* ICMP_ADVLENPREF is the preferred number of bytes which should be contiguous.
|
||||
* SCTP needs additional 12 bytes to be able to access the initiate tag
|
||||
* in packets containing an INIT chunk.
|
||||
* in packets containing an INIT chunk. For also supporting SCTP/UDP,
|
||||
* additional 8 bytes are needed.
|
||||
*/
|
||||
#define ICMP_ADVLENPREF(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8 + 12)
|
||||
#define ICMP_ADVLENPREF(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8 + 8 + 12)
|
||||
|
||||
/*
|
||||
* Definition of type and code field values.
|
||||
|
@ -52,6 +52,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/udp_var.h>
|
||||
#include <sys/proc.h>
|
||||
#ifdef INET6
|
||||
#include <netinet/icmp6.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef KTR_SCTP
|
||||
@ -6880,6 +6883,261 @@ sctp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *inp,
|
||||
m_freem(m);
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
static void
|
||||
sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, void *vip, void *ctx SCTP_UNUSED)
|
||||
{
|
||||
struct ip *outer_ip, *inner_ip;
|
||||
struct sctphdr *sh;
|
||||
struct icmp *icmp;
|
||||
struct udphdr *udp;
|
||||
struct sctp_inpcb *inp;
|
||||
struct sctp_tcb *stcb;
|
||||
struct sctp_nets *net;
|
||||
struct sctp_init_chunk *ch;
|
||||
struct sockaddr_in src, dst;
|
||||
uint8_t type, code;
|
||||
|
||||
inner_ip = (struct ip *)vip;
|
||||
icmp = (struct icmp *)((caddr_t)inner_ip -
|
||||
(sizeof(struct icmp) - sizeof(struct ip)));
|
||||
outer_ip = (struct ip *)((caddr_t)icmp - sizeof(struct ip));
|
||||
if (ntohs(outer_ip->ip_len) <
|
||||
sizeof(struct ip) + 8 + (inner_ip->ip_hl << 2) + sizeof(struct udphdr) + 8) {
|
||||
return;
|
||||
}
|
||||
udp = (struct udphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2));
|
||||
sh = (struct sctphdr *)(udp + 1);
|
||||
memset(&src, 0, sizeof(struct sockaddr_in));
|
||||
src.sin_family = AF_INET;
|
||||
src.sin_len = sizeof(struct sockaddr_in);
|
||||
src.sin_port = sh->src_port;
|
||||
src.sin_addr = inner_ip->ip_src;
|
||||
memset(&dst, 0, sizeof(struct sockaddr_in));
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_len = sizeof(struct sockaddr_in);
|
||||
dst.sin_port = sh->dest_port;
|
||||
dst.sin_addr = inner_ip->ip_dst;
|
||||
/*
|
||||
* 'dst' holds the dest of the packet that failed to be sent. 'src'
|
||||
* holds our local endpoint address. Thus we reverse the dst and the
|
||||
* src in the lookup.
|
||||
*/
|
||||
inp = NULL;
|
||||
net = NULL;
|
||||
stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst,
|
||||
(struct sockaddr *)&src,
|
||||
&inp, &net, 1,
|
||||
SCTP_DEFAULT_VRFID);
|
||||
if ((stcb != NULL) &&
|
||||
(net != NULL) &&
|
||||
(inp != NULL) &&
|
||||
(inp->sctp_socket != NULL)) {
|
||||
/* Check the UDP port numbers */
|
||||
if ((udp->uh_dport != net->port) ||
|
||||
(udp->uh_sport != htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)))) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
/* Check the verification tag */
|
||||
if (ntohl(sh->v_tag) != 0) {
|
||||
/*
|
||||
* This must be the verification tag used for
|
||||
* sending out packets. We don't consider packets
|
||||
* reflecting the verification tag.
|
||||
*/
|
||||
if (ntohl(sh->v_tag) != stcb->asoc.peer_vtag) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (ntohs(outer_ip->ip_len) >=
|
||||
sizeof(struct ip) +
|
||||
8 + (inner_ip->ip_hl << 2) + 8 + 20) {
|
||||
/*
|
||||
* In this case we can check if we got an
|
||||
* INIT chunk and if the initiate tag
|
||||
* matches.
|
||||
*/
|
||||
ch = (struct sctp_init_chunk *)(sh + 1);
|
||||
if ((ch->ch.chunk_type != SCTP_INITIATION) ||
|
||||
(ntohl(ch->init.initiate_tag) != stcb->asoc.my_vtag)) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
type = icmp->icmp_type;
|
||||
code = icmp->icmp_code;
|
||||
if ((type = ICMP_UNREACH) &&
|
||||
(code = ICMP_UNREACH_PORT)) {
|
||||
code = ICMP_UNREACH_PROTOCOL;
|
||||
}
|
||||
sctp_notify(inp, stcb, net, type, code,
|
||||
ntohs(inner_ip->ip_len),
|
||||
ntohs(icmp->icmp_nextmtu));
|
||||
} else {
|
||||
if ((stcb == NULL) && (inp != NULL)) {
|
||||
/* reduce ref-count */
|
||||
SCTP_INP_WLOCK(inp);
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
}
|
||||
if (stcb) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
static void
|
||||
sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx SCTP_UNUSED)
|
||||
{
|
||||
struct ip6ctlparam *ip6cp;
|
||||
struct sctp_inpcb *inp;
|
||||
struct sctp_tcb *stcb;
|
||||
struct sctp_nets *net;
|
||||
struct sctphdr sh;
|
||||
struct udphdr udp;
|
||||
struct sockaddr_in6 src, dst;
|
||||
uint8_t type, code;
|
||||
|
||||
ip6cp = (struct ip6ctlparam *)d;
|
||||
/*
|
||||
* XXX: We assume that when IPV6 is non NULL, M and OFF are valid.
|
||||
*/
|
||||
if (ip6cp->ip6c_m == NULL) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Check if we can safely examine the ports and the verification tag
|
||||
* of the SCTP common header.
|
||||
*/
|
||||
if (ip6cp->ip6c_m->m_pkthdr.len <
|
||||
ip6cp->ip6c_off + sizeof(struct udphdr) + offsetof(struct sctphdr, checksum)) {
|
||||
return;
|
||||
}
|
||||
/* Copy out the UDP header. */
|
||||
memset(&udp, 0, sizeof(struct udphdr));
|
||||
m_copydata(ip6cp->ip6c_m,
|
||||
ip6cp->ip6c_off,
|
||||
sizeof(struct udphdr),
|
||||
(caddr_t)&udp);
|
||||
/* Copy out the port numbers and the verification tag. */
|
||||
memset(&sh, 0, sizeof(struct sctphdr));
|
||||
m_copydata(ip6cp->ip6c_m,
|
||||
ip6cp->ip6c_off + sizeof(struct udphdr),
|
||||
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t),
|
||||
(caddr_t)&sh);
|
||||
memset(&src, 0, sizeof(struct sockaddr_in6));
|
||||
src.sin6_family = AF_INET6;
|
||||
src.sin6_len = sizeof(struct sockaddr_in6);
|
||||
src.sin6_port = sh.src_port;
|
||||
src.sin6_addr = ip6cp->ip6c_ip6->ip6_src;
|
||||
if (in6_setscope(&src.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) {
|
||||
return;
|
||||
}
|
||||
memset(&dst, 0, sizeof(struct sockaddr_in6));
|
||||
dst.sin6_family = AF_INET6;
|
||||
dst.sin6_len = sizeof(struct sockaddr_in6);
|
||||
dst.sin6_port = sh.dest_port;
|
||||
dst.sin6_addr = ip6cp->ip6c_ip6->ip6_dst;
|
||||
if (in6_setscope(&dst.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) {
|
||||
return;
|
||||
}
|
||||
inp = NULL;
|
||||
net = NULL;
|
||||
stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst,
|
||||
(struct sockaddr *)&src,
|
||||
&inp, &net, 1, SCTP_DEFAULT_VRFID);
|
||||
if ((stcb != NULL) &&
|
||||
(net != NULL) &&
|
||||
(inp != NULL) &&
|
||||
(inp->sctp_socket != NULL)) {
|
||||
/* Check the UDP port numbers */
|
||||
if ((udp.uh_dport != net->port) ||
|
||||
(udp.uh_sport != htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)))) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
/* Check the verification tag */
|
||||
if (ntohl(sh.v_tag) != 0) {
|
||||
/*
|
||||
* This must be the verification tag used for
|
||||
* sending out packets. We don't consider packets
|
||||
* reflecting the verification tag.
|
||||
*/
|
||||
if (ntohl(sh.v_tag) != stcb->asoc.peer_vtag) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (ip6cp->ip6c_m->m_pkthdr.len >=
|
||||
ip6cp->ip6c_off + sizeof(struct udphdr) +
|
||||
sizeof(struct sctphdr) +
|
||||
sizeof(struct sctp_chunkhdr) +
|
||||
offsetof(struct sctp_init, a_rwnd)) {
|
||||
/*
|
||||
* In this case we can check if we got an
|
||||
* INIT chunk and if the initiate tag
|
||||
* matches.
|
||||
*/
|
||||
uint32_t initiate_tag;
|
||||
uint8_t chunk_type;
|
||||
|
||||
m_copydata(ip6cp->ip6c_m,
|
||||
ip6cp->ip6c_off +
|
||||
sizeof(struct udphdr) +
|
||||
sizeof(struct sctphdr),
|
||||
sizeof(uint8_t),
|
||||
(caddr_t)&chunk_type);
|
||||
m_copydata(ip6cp->ip6c_m,
|
||||
ip6cp->ip6c_off +
|
||||
sizeof(struct udphdr) +
|
||||
sizeof(struct sctphdr) +
|
||||
sizeof(struct sctp_chunkhdr),
|
||||
sizeof(uint32_t),
|
||||
(caddr_t)&initiate_tag);
|
||||
if ((chunk_type != SCTP_INITIATION) ||
|
||||
(ntohl(initiate_tag) != stcb->asoc.my_vtag)) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
type = ip6cp->ip6c_icmp6->icmp6_type;
|
||||
code = ip6cp->ip6c_icmp6->icmp6_code;
|
||||
if ((type == ICMP6_DST_UNREACH) &&
|
||||
(code == ICMP6_DST_UNREACH_NOPORT)) {
|
||||
type = ICMP6_PARAM_PROB;
|
||||
code = ICMP6_PARAMPROB_NEXTHEADER;
|
||||
}
|
||||
sctp6_notify(inp, stcb, net, type, code,
|
||||
(uint16_t) ntohl(ip6cp->ip6c_icmp6->icmp6_mtu));
|
||||
} else {
|
||||
if ((stcb == NULL) && (inp != NULL)) {
|
||||
/* reduce inp's ref-count */
|
||||
SCTP_INP_WLOCK(inp);
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
}
|
||||
if (stcb) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
sctp_over_udp_stop(void)
|
||||
{
|
||||
@ -6945,7 +7203,9 @@ sctp_over_udp_start(void)
|
||||
}
|
||||
/* Call the special UDP hook. */
|
||||
if ((ret = udp_set_kernel_tunneling(SCTP_BASE_INFO(udp4_tun_socket),
|
||||
sctp_recv_udp_tunneled_packet, NULL, NULL))) {
|
||||
sctp_recv_udp_tunneled_packet,
|
||||
sctp_recv_icmp_tunneled_packet,
|
||||
NULL))) {
|
||||
sctp_over_udp_stop();
|
||||
return (ret);
|
||||
}
|
||||
@ -6969,7 +7229,9 @@ sctp_over_udp_start(void)
|
||||
}
|
||||
/* Call the special UDP hook. */
|
||||
if ((ret = udp_set_kernel_tunneling(SCTP_BASE_INFO(udp6_tun_socket),
|
||||
sctp_recv_udp_tunneled_packet, NULL, NULL))) {
|
||||
sctp_recv_udp_tunneled_packet,
|
||||
sctp_recv_icmp6_tunneled_packet,
|
||||
NULL))) {
|
||||
sctp_over_udp_stop();
|
||||
return (ret);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user