From 8f201b88beb0e67b4fc23f66eae1fc65fb76a591 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Tue, 29 May 2018 08:14:38 +0200 Subject: [PATCH 01/11] BasicChanges --- usrsctplib/netinet/sctp_output.c | 9 ++++----- usrsctplib/user_mbuf.c | 24 ++++++++++++++++++++++++ usrsctplib/user_mbuf.h | 2 ++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 8490d1e88..007fd8d03 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -13046,7 +13046,7 @@ sctp_send_operr_to(struct sockaddr *src, struct sockaddr *dst, static struct mbuf * sctp_copy_resume(struct uio *uio, int max_send_len, -#if defined(__FreeBSD__) && __FreeBSD_version > 602000 +#if defined(__FreeBSD__) && __FreeBSD_version > 602000 || defined(__Userspace__) int user_marks_eor, #endif int *error, @@ -13066,9 +13066,8 @@ sctp_copy_resume(struct uio *uio, *new_tail = m_last(m); } return (m); -#elif defined(__FreeBSD__) && __FreeBSD_version > 602000 +#elif defined(__FreeBSD__) && __FreeBSD_version > 602000 || defined(__Userspace__) struct mbuf *m; - m = m_uiotombuf(uio, M_WAITOK, max_send_len, 0, (M_PKTHDR | (user_marks_eor ? M_EOR : 0))); if (m == NULL) { @@ -13159,7 +13158,7 @@ sctp_copy_one(struct sctp_stream_queue_pending *sp, sp->tail_mbuf = m_last(sp->data); return (0); -#elif defined(__FreeBSD__) && __FreeBSD_version > 602000 +#elif defined(__FreeBSD__) && __FreeBSD_version > 602000 || defined(__Userspace__) sp->data = m_uiotombuf(uio, M_WAITOK, sp->length, resv_upfront, 0); if (sp->data == NULL) { @@ -14274,7 +14273,7 @@ sctp_lower_sosend(struct socket *so, #if defined(__APPLE__) SCTP_SOCKET_UNLOCK(so, 0); #endif -#if defined(__FreeBSD__) && __FreeBSD_version > 602000 +#if defined(__FreeBSD__) && __FreeBSD_version > 602000 || defined(__Userspace__) mm = sctp_copy_resume(uio, max_len, user_marks_eor, &error, &sndout, &new_tail); #else mm = sctp_copy_resume(uio, max_len, &error, &sndout, &new_tail); diff --git a/usrsctplib/user_mbuf.c b/usrsctplib/user_mbuf.c index a34a2136d..60df83a65 100755 --- a/usrsctplib/user_mbuf.c +++ b/usrsctplib/user_mbuf.c @@ -420,6 +420,30 @@ m_uiotombuf(struct uio *uio, int how, int len, int align, int flags) return (m); } +u_int +m_length(struct mbuf *m0, struct mbuf **last) +{ + struct mbuf *m; + u_int len; + + len = 0; + for (m = m0; m != NULL; m = m->m_next) { + len += m->m_len; + if (m->m_next == NULL) + break; + } + if (last != NULL) + *last = m; + return (len); +} + +struct mbuf * +m_last(struct mbuf *m) +{ + while (m->m_next) + m = m->m_next; + return (m); +} /* * Unlink a tag from the list of tags associated with an mbuf. diff --git a/usrsctplib/user_mbuf.h b/usrsctplib/user_mbuf.h index 58a5daf6b..44ed73dc6 100755 --- a/usrsctplib/user_mbuf.h +++ b/usrsctplib/user_mbuf.h @@ -57,6 +57,8 @@ struct mbuf * m_free(struct mbuf *m); void m_clget(struct mbuf *m, int how); struct mbuf * m_getm2(struct mbuf *m, int len, int how, short type, int flags, int allonebuf); struct mbuf *m_uiotombuf(struct uio *uio, int how, int len, int align, int flags); +struct mbuf *m_last(struct mbuf *m); +u_int m_length(struct mbuf *m0, struct mbuf **last); /* mbuf initialization function */ From dbb616b8333f5071e11641fafc1e5f039ca71610 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Tue, 29 May 2018 11:48:44 +0200 Subject: [PATCH 02/11] icmp4Neu --- usrsctplib/netinet/sctp_output.c | 17 +- usrsctplib/netinet/sctp_pcb.c | 100 ++++++++++++ usrsctplib/netinet/sctp_pcb.h | 5 + usrsctplib/netinet/sctp_structs.h | 1 + usrsctplib/netinet/sctp_usrreq.c | 36 +++-- usrsctplib/netinet/sctp_var.h | 11 +- usrsctplib/netinet/sctputil.c | 14 +- usrsctplib/netinet/sctputil.h | 4 +- usrsctplib/user_recv_thread.c | 250 ++++++++++++++++++++++++++++++ 9 files changed, 416 insertions(+), 22 deletions(-) diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 007fd8d03..64bd2f27b 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4477,9 +4477,13 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if ((ro->ro_rt != NULL) && (net->ro._s_addr) && ((net->dest_state & SCTP_ADDR_NO_PMTUD) == 0)) { uint32_t mtu; - +#if defined(__Userspace__) + mtu = sctp_get_mtu_from_addr(inp, (struct sockaddr *)&(net->ro._s_addr->address.sin)); +#else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); - if (mtu > 0) { +#endif + + if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { if (net->port) { mtu -= sizeof(struct udphdr); } @@ -4487,6 +4491,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, sctp_mtu_size_reset(inp, &stcb->asoc, mtu); } net->mtu = mtu; + net->got_max = 1; } } else if (ro->ro_rt == NULL) { /* route was freed */ @@ -4939,9 +4944,12 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if ((ro->ro_rt != NULL) && (net->ro._s_addr) && ((net->dest_state & SCTP_ADDR_NO_PMTUD) == 0)) { uint32_t mtu; - +#if defined(__Userspace__) + mtu = sctp_get_mtu_from_addr(inp, (struct sockaddr *)&(net->ro._s_addr->address.sin)); +#else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); - if (mtu > 0) { +#endif + if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { if (net->port) { mtu -= sizeof(struct udphdr); } @@ -4949,6 +4957,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, sctp_mtu_size_reset(inp, &stcb->asoc, mtu); } net->mtu = mtu; + net->got_max = 1; } } #if !defined(__Panda__) && !defined(__Userspace__) diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index c83450388..ac4c8dca0 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -165,6 +165,102 @@ sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb) SCTP_INP_INFO_RUNLOCK(); } + +#if 0 +#if defined (__Userspace_os_Windows) +int +sctp_get_mtu_from_addr(struct sockaddr *sa) +{ + int mtu = 0; +#if defined(INET) || defined(INET6) + int ret; + unsigned int i = 0; + DWORD Err, AdapterAddrsSize; + PIP_ADAPTER_ADDRESSES pAdapterAddrs, pAdapt; + ret = 0; + AdapterAddrsSize = 0; + pAdapterAddrs = NULL; + if ((Err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &AdapterAddrsSize)) != 0) { + if ((Err != ERROR_BUFFER_OVERFLOW) && (Err != ERROR_INSUFFICIENT_BUFFER)) { + SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() sizing failed with error code %d and AdapterAddrsSize = %d\n", Err, AdapterAddrsSize); + ret = -1; + goto cleanup; + } + } + + /* Allocate memory from sizing information */ + if ((pAdapterAddrs = (PIP_ADAPTER_ADDRESSES) GlobalAlloc(GPTR, AdapterAddrsSize)) == NULL) { + SCTPDBG(SCTP_DEBUG_USR, "Memory allocation error!\n"); + return -1; + } + /* Get actual adapter information */ + if ((Err = GetAdaptersAddresses(AF_INET, 0, NULL, pAdapterAddrs, &AdapterAddrsSize)) != ERROR_SUCCESS) { + SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() failed with error code %d\n", Err); + ret = -1; + goto cleanup; + } + for (pAdapt = pAdapterAddrs; pAdapt; pAdapt = pAdapt->Next) { + struct sockaddr *addr = (struct sockaddr *)pAdapt->FirstUnicastAddress->Address.lpSockaddr; + if (sa->sa_family != addr->sa_family) { + continue; + } + SCTPDBG(SCTP_DEBUG_OUTPUT3, "Compare to address: "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT3, (struct sockaddr *)(addr)); + switch (sa->sa_family) { + case AF_INET: + if (memcmp(((const void *)&((struct sockaddr_in *)addr)->sin_addr), ((void *)&((struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr)) == 0) { + mtu = pAdapt->Mtu; + goto cleanup; + } + break; + case AF_INET6: + if (memcmp(((const void *)&((struct sockaddr_in6 *)addr)->sin6_addr), ((void *)&((struct sockaddr_in6 *)sa)->sin6_addr), sizeof(struct in_addr)) == 0) { + mtu = pAdapt->Mtu; + goto cleanup; + } + break; + default: + printf("Address family not supported\n"); + } + } +cleanup: + if (pAdapterAddrs != NULL) { + GlobalFree(pAdapterAddrs); + } +#endif + return mtu; +} +#endif +#endif +//#if defined(__Userspace__) && !defined(__Userspace_os_NaCl) +int +sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa) +{ +#if defined(INET) || defined(INET6) + struct sctp_laddr *laddr; + + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa->address.sa.sa_family != sa->sa_family) { + continue; + } + switch (sa->sa_family) { + case AF_INET: + if (memcmp(((const void *)&((struct sockaddr_in *)&laddr->ifa->address.sin)->sin_addr), ((void *)&((struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr)) == 0) { + return (laddr->ifa->ifa_mtu); + } + break; + case AF_INET6: + if (memcmp(((const void *)&((struct sockaddr_in6 *)&laddr->ifa->address.sin6)->sin6_addr), ((void *)&((struct sockaddr_in6 *)sa)->sin6_addr), sizeof(struct in6_addr)) == 0) { + return (laddr->ifa->ifa_mtu); + } + break; + } + } +#endif + return 0; +} +//#endif + /*- * Addresses are added to VRF's (Virtual Router's). For BSD we * have only the default VRF 0. We maintain a hash list of @@ -622,6 +718,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { + sctp_ifap->ifa_mtu = sctp_ifnp->ifn_mtu; /* Hmm, it already exists? */ if ((sctp_ifap->ifn_p) && (sctp_ifap->ifn_p->ifn_index == ifn_index)) { @@ -706,6 +803,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, #endif sctp_ifap->localifa_flags = SCTP_ADDR_VALID | SCTP_ADDR_DEFER_USE; sctp_ifap->flags = ifa_flags; + sctp_ifap->ifa_mtu = sctp_ifnp->ifn_mtu; /* Set scope */ switch (sctp_ifap->address.sa.sa_family) { #ifdef INET @@ -4590,6 +4688,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, #ifdef INET6 net->flowlabel = stcb->asoc.default_flowlabel; #endif + net->got_max = 0; if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) { net->dest_state |= SCTP_ADDR_NOHB; } else { @@ -4693,6 +4792,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, SCTP_SET_MTU_OF_ROUTE(&net->ro._l_addr.sa, net->ro.ro_rt, net->mtu); } + net->got_max = 1; } } #endif diff --git a/usrsctplib/netinet/sctp_pcb.h b/usrsctplib/netinet/sctp_pcb.h index 34881863f..3afe6fa43 100755 --- a/usrsctplib/netinet/sctp_pcb.h +++ b/usrsctplib/netinet/sctp_pcb.h @@ -115,6 +115,7 @@ struct sctp_ifa { uint32_t flags; uint32_t localifa_flags; uint32_t vrf_id; /* vrf_id of this addr (for deleting) */ + uint32_t ifa_mtu; uint8_t src_is_loop; uint8_t src_is_priv; uint8_t src_is_glob; @@ -323,10 +324,13 @@ struct sctp_base_info { #if defined(__Userspace_os_Windows) SOCKET userspace_rawsctp; SOCKET userspace_udpsctp; + SOCKET userspace_icmp; #else int userspace_rawsctp; int userspace_udpsctp; + int userspace_icmp; #endif + userland_thread_t recvthreadicmp; userland_thread_t recvthreadraw; userland_thread_t recvthreadudp; #endif @@ -609,6 +613,7 @@ int register_recv_cb (struct socket *, struct sctp_rcvinfo, int, void *)); int register_send_cb (struct socket *, uint32_t, int (*)(struct socket *, uint32_t)); int register_ulp_info (struct socket *, void *); +int sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa); #endif struct sctp_tcb { diff --git a/usrsctplib/netinet/sctp_structs.h b/usrsctplib/netinet/sctp_structs.h index f49f2c87a..e0e37b0df 100755 --- a/usrsctplib/netinet/sctp_structs.h +++ b/usrsctplib/netinet/sctp_structs.h @@ -447,6 +447,7 @@ struct sctp_nets { uint32_t flowid; uint8_t flowtype; #endif + uint8_t got_max; }; diff --git a/usrsctplib/netinet/sctp_usrreq.c b/usrsctplib/netinet/sctp_usrreq.c index 8f2b40024..5195b4824 100755 --- a/usrsctplib/netinet/sctp_usrreq.c +++ b/usrsctplib/netinet/sctp_usrreq.c @@ -58,7 +58,8 @@ __FBSDID("$FreeBSD: head/sys/netinet/sctp_usrreq.c 332273 2018-04-08 12:08:20Z t #include #if defined(__Userspace__) #include -#else +#endif +#if !defined(__Userspace_os_Windows) #include #endif @@ -141,6 +142,7 @@ sctp_init(void) #ifdef INET SCTP_BASE_VAR(userspace_rawsctp) = -1; SCTP_BASE_VAR(userspace_udpsctp) = -1; + SCTP_BASE_VAR(userspace_icmp) = -1; #endif #ifdef INET6 SCTP_BASE_VAR(userspace_rawsctp6) = -1; @@ -197,6 +199,16 @@ sctp_finish(void) } #endif #endif +#ifdef INET + if (SCTP_BASE_VAR(userspace_icmp) != -1) { +#if defined(__Userspace_os_Windows) + WaitForSingleObject(SCTP_BASE_VAR(recvthreadicmp), INFINITE); + CloseHandle(SCTP_BASE_VAR(recvthreadicmp)); +#else + pthread_join(SCTP_BASE_VAR(recvthreadicmp), NULL); +#endif + } +#endif #ifdef INET if (SCTP_BASE_VAR(userspace_rawsctp) != -1) { #if defined(__Userspace_os_Windows) @@ -294,7 +306,6 @@ sctp_pathmtu_adjustment(struct sctp_tcb *stcb, uint16_t nxtsz) } #ifdef INET -#if !defined(__Userspace__) void sctp_notify(struct sctp_inpcb *inp, struct sctp_tcb *stcb, @@ -407,10 +418,9 @@ sctp_notify(struct sctp_inpcb *inp, SCTP_TCB_UNLOCK(stcb); } } -#endif -#if !defined(__Panda__) && !defined(__Userspace__) -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) +#if !defined(__Panda__) +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) void #else void * @@ -421,7 +431,7 @@ sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip, struct ifnet *ifp SCTP_UN sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip) #endif { -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__Userspace__) struct ip *outer_ip; #endif struct ip *inner_ip; @@ -430,33 +440,35 @@ sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip) struct sctp_inpcb *inp; struct sctp_tcb *stcb; struct sctp_nets *net; -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__Userspace__) struct sctp_init_chunk *ch; #endif struct sockaddr_in src, dst; if (sa->sa_family != AF_INET || ((struct sockaddr_in *)sa)->sin_addr.s_addr == INADDR_ANY) { -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) return; #else return (NULL); #endif } +#if !defined(__Userspace__) if (PRC_IS_REDIRECT(cmd)) { vip = NULL; } else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) { -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) return; #else return (NULL); #endif } +#endif if (vip != NULL) { inner_ip = (struct ip *)vip; icmp = (struct icmp *)((caddr_t)inner_ip - (sizeof(struct icmp) - sizeof(struct ip))); -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined (__Userspace__) outer_ip = (struct ip *)((caddr_t)icmp - sizeof(struct ip)); #endif sh = (struct sctphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2)); @@ -500,7 +512,7 @@ sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip) return; } } else { -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__Userspace__) if (ntohs(outer_ip->ip_len) >= sizeof(struct ip) + 8 + (inner_ip->ip_hl << 2) + 20) { @@ -555,7 +567,7 @@ sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip) } } } -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) return; #else return (NULL); diff --git a/usrsctplib/netinet/sctp_var.h b/usrsctplib/netinet/sctp_var.h index bc4afe95f..0b12eef90 100755 --- a/usrsctplib/netinet/sctp_var.h +++ b/usrsctplib/netinet/sctp_var.h @@ -409,7 +409,7 @@ void sctp_close(struct socket *so); int sctp_detach(struct socket *so); #endif int sctp_disconnect(struct socket *so); -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) #if defined(__FreeBSD__) && __FreeBSD_version < 902000 void sctp_ctlinput __P((int, struct sockaddr *, void *)); int sctp_ctloutput __P((struct socket *, struct sockopt *)); @@ -423,8 +423,13 @@ void sctp_pathmtu_adjustment __P((struct sctp_tcb *, uint16_t)); void sctp_ctlinput(int, struct sockaddr *, void *, struct ifnet * SCTP_UNUSED); #else void sctp_ctlinput(int, struct sockaddr *, void *); -#endif +#if defined(__Userspace__) +int sctp_ctloutput(int, struct socket *, int, int, struct mbuf **); +#else int sctp_ctloutput(struct socket *, struct sockopt *); +#endif +#endif + #ifdef INET void sctp_input_with_port(struct mbuf *, int, uint16_t); #if defined(__FreeBSD__) && __FreeBSD_version >= 1100020 @@ -455,6 +460,8 @@ void sctp_drain(void); void sctp_init(uint16_t, int (*)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df), void (*)(const char *, ...)); +void sctp_notify(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, + uint8_t, uint8_t, uint16_t, uint32_t); #elif defined(__FreeBSD__) && __FreeBSD_version < 902000 void sctp_init __P((void)); #elif defined(__APPLE__) && (!defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) &&!defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION)) diff --git a/usrsctplib/netinet/sctputil.c b/usrsctplib/netinet/sctputil.c index 1134b5065..33e62efd6 100755 --- a/usrsctplib/netinet/sctputil.c +++ b/usrsctplib/netinet/sctputil.c @@ -57,6 +57,9 @@ __FBSDID("$FreeBSD: head/sys/netinet/sctputil.c 334286 2018-05-28 13:31:47Z tuex #include #if defined(__Userspace__) #include +#if !defined(__Userspace_os_Windows) +#include +#endif #endif #if defined(__FreeBSD__) #if defined(INET6) || defined(INET) @@ -7771,10 +7774,11 @@ sctp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *inp, m_freem(m); } #endif +#endif -#if __FreeBSD_version >= 1100000 +#if defined(__Userspace__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1100000) #ifdef INET -static void +void sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, void *vip, void *ctx SCTP_UNUSED) { struct ip *outer_ip, *inner_ip; @@ -7794,6 +7798,7 @@ sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, void *vip, void *ct 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) { + SCTPDBG(SCTP_DEBUG_USR, "Packet too short!!\n"); return; } udp = (struct udphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2)); @@ -7897,7 +7902,9 @@ sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, void *vip, void *ct return; } #endif +#endif +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100000 #ifdef INET6 static void sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx SCTP_UNUSED) @@ -8059,6 +8066,7 @@ sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx #endif #endif +#if defined(__FreeBSD__) void sctp_over_udp_stop(void) { @@ -8121,7 +8129,7 @@ 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, -#if __FreeBSD_version >= 1100000 +#if __FreeBSD_version >= 1100000 || defined(__Userspace__) sctp_recv_icmp_tunneled_packet, #endif NULL))) { diff --git a/usrsctplib/netinet/sctputil.h b/usrsctplib/netinet/sctputil.h index 69fe48498..cd99fe33c 100755 --- a/usrsctplib/netinet/sctputil.h +++ b/usrsctplib/netinet/sctputil.h @@ -121,7 +121,9 @@ void sctp_invoke_recv_callback(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_queued_to_read *, int); - +void +sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, + void *vip, void *ctx SCTP_UNUSED); #endif void sctp_add_to_readq(struct sctp_inpcb *inp, diff --git a/usrsctplib/user_recv_thread.c b/usrsctplib/user_recv_thread.c index ea0c10efd..1cff8fd4f 100755 --- a/usrsctplib/user_recv_thread.c +++ b/usrsctplib/user_recv_thread.c @@ -45,6 +45,7 @@ #include #include #include +#include #if 0 #if defined(__Userspace_os_Linux) #include @@ -56,9 +57,14 @@ #endif #endif #endif +#if defined(__Userspace__) +#if !defined(__Userspace_os_Windows) +#include +#endif #if defined(__Userspace_os_Darwin) || defined(__Userspace_os_DragonFly) || defined(__Userspace_os_FreeBSD) #include #endif +#endif /* local macros and datatypes used to get IP addresses system independently */ #if !defined(IP_PKTINFO ) && ! defined(IP_RECVDSTADDR) # error "Can't determine socket option to use to get UDP IP" @@ -258,6 +264,176 @@ recv_function_route(void *arg) } #endif +#ifdef INET +void +ip_stripoptions(struct mbuf *m) +{ + struct ip *ip = mtod(m, struct ip *); + int olen; + + olen = (ip->ip_hl << 2) - sizeof(struct ip); + m->m_len -= olen; + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len -= olen; + ip->ip_len = htons(ntohs(ip->ip_len) - olen); + ip->ip_hl = sizeof(struct ip) >> 2; + bcopy((char *)ip + sizeof(struct ip) + olen, (ip + 1), + (size_t )(m->m_len - sizeof(struct ip))); +} + + +static void * +recv_function_icmp(void *arg) +{ + struct mbuf **recvmbuf, *m; + struct ip *ip, *inner_ip; + struct icmp *icmp; + struct sockaddr_in icmpsrc; + struct sockaddr_in src, dst; +#if !defined(__Userspace_os_Windows) + struct msghdr msg; + struct iovec recv_iovec[MAXLEN_MBUF_CHAIN]; +#else + WSABUF recv_iovec[MAXLEN_MBUF_CHAIN]; + int nResult, m_ErrorCode; + DWORD flags; + struct sockaddr_in from; + int fromlen; +#endif + /*Initially the entire set of mbufs is to be allocated. + to_fill indicates this amount. */ + int to_fill = MAXLEN_MBUF_CHAIN; + /* iovlen is the size of each mbuf in the chain */ + int i, n, ncounter = 0; + int iovlen = MCLBYTES; + int want_ext = (iovlen > MLEN)? 1 : 0; + int want_header = 0; + + sctp_userspace_set_threadname("IP4/ICMP rcv"); + + bzero((void *)&src, sizeof(struct sockaddr_in)); + bzero((void *)&dst, sizeof(struct sockaddr_in)); + + recvmbuf = malloc(sizeof(struct mbuf *) * MAXLEN_MBUF_CHAIN); + + while (1) { + for (i = 0; i < to_fill; i++) { + /* Not getting the packet header. Tests with chain of one run + as usual without having the packet header. + Have tried both sending and receiving + */ + recvmbuf[i] = sctp_get_mbuf_for_msg(iovlen, want_header, M_NOWAIT, want_ext, MT_DATA); +#if !defined(__Userspace_os_Windows) + recv_iovec[i].iov_base = (caddr_t)recvmbuf[i]->m_data; + recv_iovec[i].iov_len = iovlen; +#else + recv_iovec[i].buf = (caddr_t)recvmbuf[i]->m_data; + recv_iovec[i].len = iovlen; +#endif + } + to_fill = 0; +#if defined(__Userspace_os_Windows) + flags = 0; + ncounter = 0; + fromlen = sizeof(struct sockaddr_in); + bzero((void *)&from, sizeof(struct sockaddr_in)); + + nResult = WSARecvFrom(SCTP_BASE_VAR(userspace_icmp), recv_iovec, MAXLEN_MBUF_CHAIN, (LPDWORD)&ncounter, (LPDWORD)&flags, (struct sockaddr*)&from, &fromlen, NULL, NULL); + if (nResult != 0) { + m_ErrorCode = WSAGetLastError(); + if (m_ErrorCode == WSAETIMEDOUT) { + continue; + } + if ((m_ErrorCode == WSAENOTSOCK) || (m_ErrorCode == WSAEINTR)) { + break; + } + } + n = ncounter; +#else + bzero((void *)&msg, sizeof(struct msghdr)); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = recv_iovec; + msg.msg_iovlen = MAXLEN_MBUF_CHAIN; + msg.msg_control = NULL; + msg.msg_controllen = 0; + ncounter = n = recvmsg(SCTP_BASE_VAR(userspace_icmp), &msg, 0); + if (n < 0) { + if (errno == EAGAIN) { + continue; + } else { + break; + } + } +#endif + SCTPDBG(SCTP_DEBUG_USR,"%d bytes received on ICMP raw socket\n", n); + + if (n <= iovlen) { + SCTP_BUF_LEN(recvmbuf[0]) = n; + (to_fill)++; + } else { + i = 0; + SCTP_BUF_LEN(recvmbuf[0]) = iovlen; + + ncounter -= iovlen; + (to_fill)++; + do { + recvmbuf[i]->m_next = recvmbuf[i+1]; + SCTP_BUF_LEN(recvmbuf[i]->m_next) = min(ncounter, iovlen); + i++; + ncounter -= iovlen; + (to_fill)++; + } while (ncounter > 0); + } + m = *recvmbuf; + ip = mtod(m, struct ip *); +#if defined(__Userspace_os_Darwin) + ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2)); +#endif + if (ntohs(ip->ip_len) != n) { + SCTPDBG(SCTP_DEBUG_USR,"IP total length does not match length of received packet\n"); + return (NULL); + } + + ip_stripoptions(m); + if (m->m_len < i && (m = m_pullup(m, i)) == NULL) { + /* This should actually not happen */ + SCTPDBG(SCTP_DEBUG_USR,"ICMP packet too short\n"); + return NULL; + } + ip = mtod(m, struct ip *); + icmp = (struct icmp *)(ip + 1); + if (icmp->icmp_type == ICMP_UNREACH || + icmp->icmp_type == ICMP_TIMXCEED || + icmp->icmp_type == ICMP_PARAMPROB) { + int icmplen = ntohs(ip->ip_len) - sizeof(struct ip); + if (icmplen < (int)ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icmp) || + icmp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) { + SCTPDBG(SCTP_DEBUG_USR,"Bad icmp packet length\n"); + return (NULL); + } + bzero(&icmpsrc, sizeof(icmpsrc)); +#ifdef HAVE_SIN_LEN + icmpsrc.sin_len = sizeof(struct sockaddr_in); +#endif + icmpsrc.sin_family = AF_INET; + inner_ip = (struct ip *)&(icmp->icmp_ip); + icmpsrc.sin_addr = icmp->icmp_ip.ip_dst; + if (inner_ip->ip_p == IPPROTO_SCTP) { + sctp_ctlinput(icmp->icmp_code, (struct sockaddr *)&icmpsrc, (void *)inner_ip); + } else if (inner_ip->ip_p == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr *)(inner_ip + 1); + uint16_t port = ntohs(udp->uh_sport); + if (port == SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) { + sctp_recv_icmp_tunneled_packet(icmp->icmp_code, (struct sockaddr *)&icmpsrc, (void *)inner_ip, NULL); + } + } + } + } + return (NULL); +} +#endif + #ifdef INET static void * recv_function_raw(void *arg) @@ -1120,6 +1296,58 @@ recv_thread_init(void) } } #endif + +#if defined(INET) + if (SCTP_BASE_VAR(userspace_icmp) == -1) { + if ((SCTP_BASE_VAR(userspace_icmp) = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't create raw socket for ICMP (errno = %d).\n", WSAGetLastError()); +#else + perror("ICMP socket"); + SCTPDBG(SCTP_DEBUG_USR, "Can't create raw socket for ICMP (errno = %d).\n", errno); +#endif + } else { + /* complete setting up the raw SCTP socket */ + if (setsockopt(SCTP_BASE_VAR(userspace_icmp), IPPROTO_IP, IP_HDRINCL,(const void*)&hdrincl, sizeof(int)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set IP_HDRINCL (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set IP_HDRINCL (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp)); +#endif + SCTP_BASE_VAR(userspace_icmp) = -1; + } else if (setsockopt(SCTP_BASE_VAR(userspace_icmp), SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set timeout on socket for ICMP/IPv4 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set timeout on socket for ICMP/IPv4 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp)); +#endif + SCTP_BASE_VAR(userspace_icmp) = -1; + } else { + memset((void *)&addr_ipv4, 0, sizeof(struct sockaddr_in)); +#ifdef HAVE_SIN_LEN + addr_ipv4.sin_len = sizeof(struct sockaddr_in); +#endif + addr_ipv4.sin_family = AF_INET; + addr_ipv4.sin_port = htons(0); + addr_ipv4.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(SCTP_BASE_VAR(userspace_icmp), (const struct sockaddr *)&addr_ipv4, sizeof(struct sockaddr_in)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't bind socket for ICMP/IPv4 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't bind socket for ICMP/IPv4 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp)); +#endif + SCTP_BASE_VAR(userspace_icmp) = -1; + } + } + } + } +#endif #if defined(INET) if (SCTP_BASE_VAR(userspace_rawsctp) == -1) { if ((SCTP_BASE_VAR(userspace_rawsctp) = socket(AF_INET, SOCK_RAW, IPPROTO_SCTP)) == -1) { @@ -1394,6 +1622,21 @@ recv_thread_init(void) } #endif #endif +#if defined(INET) + if (SCTP_BASE_VAR(userspace_icmp) != -1) { + int rc; + + if ((rc = sctp_userspace_thread_create(&SCTP_BASE_VAR(recvthreadicmp), &recv_function_icmp))) { + SCTPDBG(SCTP_DEBUG_USR, "Can't start ICMP/IPv4 recv thread (%d).\n", rc); +#if defined(__Userspace_os_Windows) + closesocket(SCTP_BASE_VAR(userspace_icmp)); +#else + close(SCTP_BASE_VAR(userspace_icmp)); +#endif + SCTP_BASE_VAR(userspace_icmp) = -1; + } + } +#endif #if defined(INET) if (SCTP_BASE_VAR(userspace_rawsctp) != -1) { int rc; @@ -1475,6 +1718,13 @@ recv_thread_destroy(void) closesocket(SCTP_BASE_VAR(userspace_udpsctp)); #else close(SCTP_BASE_VAR(userspace_udpsctp)); +#endif + } + if (SCTP_BASE_VAR(userspace_icmp) != -1) { +#if defined(__Userspace_os_Windows) + closesocket(SCTP_BASE_VAR(userspace_icmp)); +#else + close(SCTP_BASE_VAR(userspace_icmp)); #endif } #endif From a0630ca2b02978efcdf46fd391ca7b706b65da8c Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Tue, 29 May 2018 12:33:55 +0200 Subject: [PATCH 03/11] Get interfaceMTU and increase MTU --- usrsctplib/netinet/sctp_input.c | 6 +- usrsctplib/netinet/sctp_output.c | 33 +++++++++- usrsctplib/netinet/sctp_pcb.c | 101 ++++++++++++++++++++++++++++- usrsctplib/netinet/sctp_pcb.h | 2 + usrsctplib/netinet/sctp_structs.h | 3 + usrsctplib/netinet/sctp_usrreq.c | 33 ++++++++-- usrsctplib/netinet/sctp_var.h | 6 +- usrsctplib/netinet6/sctp6_usrreq.c | 2 +- 8 files changed, 170 insertions(+), 16 deletions(-) diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c index d9aa04b37..120eabf4d 100755 --- a/usrsctplib/netinet/sctp_input.c +++ b/usrsctplib/netinet/sctp_input.c @@ -5757,7 +5757,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt /* UDP encapsulation turned on. */ net->mtu -= sizeof(struct udphdr); if (stcb->asoc.smallest_mtu > net->mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } } else if (port == 0) { /* UDP encapsulation turned off. */ @@ -5798,7 +5798,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt /* UDP encapsulation turned on. */ net->mtu -= sizeof(struct udphdr); if (stcb->asoc.smallest_mtu > net->mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } } else if (port == 0) { /* UDP encapsulation turned off. */ @@ -5903,7 +5903,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt /* UDP encapsulation turned on. */ net->mtu -= sizeof(struct udphdr); if (stcb->asoc.smallest_mtu > net->mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } } else if (port == 0) { /* UDP encapsulation turned off. */ diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 007fd8d03..491fd8d55 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4478,6 +4478,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, ((net->dest_state & SCTP_ADDR_NO_PMTUD) == 0)) { uint32_t mtu; +#if defined(__Userspace__) + mtu = sctp_get_mtu_from_addr(inp, (struct sockaddr *)&(net->ro._s_addr->address.sin)); + if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { + if (net->port) { + mtu -= sizeof(struct udphdr); + } + if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + } + net->mtu = mtu; + net->got_max = 1; + sctp_pathmtu_adjustment(stcb, net->mtu, net); + } +#else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); if (mtu > 0) { if (net->port) { @@ -4488,6 +4502,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } net->mtu = mtu; } +#endif } else if (ro->ro_rt == NULL) { /* route was freed */ if (net->ro._s_addr && @@ -4940,7 +4955,22 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, ((net->dest_state & SCTP_ADDR_NO_PMTUD) == 0)) { uint32_t mtu; +#if defined(__Userspace__) + mtu = sctp_get_mtu_from_addr(inp, (struct sockaddr *)&(net->ro._s_addr->address.sin)); + if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { + if (net->port) { + mtu -= sizeof(struct udphdr); + } + if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + } + net->mtu = mtu; + net->got_max = 1; + sctp_pathmtu_adjustment(stcb, net->mtu, net); + } +#else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); +#endif if (mtu > 0) { if (net->port) { mtu -= sizeof(struct udphdr); @@ -4950,6 +4980,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } net->mtu = mtu; } +#endif } #if !defined(__Panda__) && !defined(__Userspace__) else if (ifp) { @@ -4968,7 +4999,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } return (ret); } -#endif + #if defined(__Userspace__) case AF_CONN: { diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index c83450388..dfbb97513 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -165,6 +165,101 @@ sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb) SCTP_INP_INFO_RUNLOCK(); } +#if 0 +#if defined (__Userspace_os_Windows) +int +sctp_get_mtu_from_addr(struct sockaddr *sa) +{ + int mtu = 0; +#if defined(INET) || defined(INET6) + int ret; + unsigned int i = 0; + DWORD Err, AdapterAddrsSize; + PIP_ADAPTER_ADDRESSES pAdapterAddrs, pAdapt; + ret = 0; + AdapterAddrsSize = 0; + pAdapterAddrs = NULL; + if ((Err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &AdapterAddrsSize)) != 0) { + if ((Err != ERROR_BUFFER_OVERFLOW) && (Err != ERROR_INSUFFICIENT_BUFFER)) { + SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() sizing failed with error code %d and AdapterAddrsSize = %d\n", Err, AdapterAddrsSize); + ret = -1; + goto cleanup; + } + } + + /* Allocate memory from sizing information */ + if ((pAdapterAddrs = (PIP_ADAPTER_ADDRESSES) GlobalAlloc(GPTR, AdapterAddrsSize)) == NULL) { + SCTPDBG(SCTP_DEBUG_USR, "Memory allocation error!\n"); + return -1; + } + /* Get actual adapter information */ + if ((Err = GetAdaptersAddresses(AF_INET, 0, NULL, pAdapterAddrs, &AdapterAddrsSize)) != ERROR_SUCCESS) { + SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() failed with error code %d\n", Err); + ret = -1; + goto cleanup; + } + for (pAdapt = pAdapterAddrs; pAdapt; pAdapt = pAdapt->Next) { + struct sockaddr *addr = (struct sockaddr *)pAdapt->FirstUnicastAddress->Address.lpSockaddr; + if (sa->sa_family != addr->sa_family) { + continue; + } + SCTPDBG(SCTP_DEBUG_OUTPUT3, "Compare to address: "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT3, (struct sockaddr *)(addr)); + switch (sa->sa_family) { + case AF_INET: + if (memcmp(((const void *)&((struct sockaddr_in *)addr)->sin_addr), ((void *)&((struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr)) == 0) { + mtu = pAdapt->Mtu; + goto cleanup; + } + break; + case AF_INET6: + if (memcmp(((const void *)&((struct sockaddr_in6 *)addr)->sin6_addr), ((void *)&((struct sockaddr_in6 *)sa)->sin6_addr), sizeof(struct in_addr)) == 0) { + mtu = pAdapt->Mtu; + goto cleanup; + } + break; + default: + printf("Address family not supported\n"); + } + } +cleanup: + if (pAdapterAddrs != NULL) { + GlobalFree(pAdapterAddrs); + } +#endif + return mtu; +} +#endif +#endif +//#if defined(__Userspace__) && !defined(__Userspace_os_NaCl) +int +sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa) +{ +#if defined(INET) || defined(INET6) + struct sctp_laddr *laddr; + + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa->address.sa.sa_family != sa->sa_family) { + continue; + } + switch (sa->sa_family) { + case AF_INET: + if (memcmp(((const void *)&((struct sockaddr_in *)&laddr->ifa->address.sin)->sin_addr), ((void *)&((struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr)) == 0) { + return (laddr->ifa->ifa_mtu); + } + break; + case AF_INET6: + if (memcmp(((const void *)&((struct sockaddr_in6 *)&laddr->ifa->address.sin6)->sin6_addr), ((void *)&((struct sockaddr_in6 *)sa)->sin6_addr), sizeof(struct in6_addr)) == 0) { + return (laddr->ifa->ifa_mtu); + } + break; + } + } +#endif + return 0; +} +//#endif + /*- * Addresses are added to VRF's (Virtual Router's). For BSD we * have only the default VRF 0. We maintain a hash list of @@ -623,6 +718,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { /* Hmm, it already exists? */ + sctp_ifap->ifa_mtu = sctp_ifnp->ifn_mtu; if ((sctp_ifap->ifn_p) && (sctp_ifap->ifn_p->ifn_index == ifn_index)) { SCTPDBG(SCTP_DEBUG_PCB4, "Using existing ifn %s (0x%x) for ifa %p\n", @@ -706,6 +802,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, #endif sctp_ifap->localifa_flags = SCTP_ADDR_VALID | SCTP_ADDR_DEFER_USE; sctp_ifap->flags = ifa_flags; + sctp_ifap->ifa_mtu = sctp_ifnp->ifn_mtu; /* Set scope */ switch (sctp_ifap->address.sa.sa_family) { #ifdef INET @@ -4695,6 +4792,8 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, } } } +#else + net->got_max = 0; #endif if (net->mtu == 0) { if (stcb->asoc.default_mtu > 0) { @@ -4754,7 +4853,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, stcb->asoc.smallest_mtu = net->mtu; } if (stcb->asoc.smallest_mtu > net->mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } #ifdef INET6 #ifdef SCTP_EMBEDDED_V6_SCOPE diff --git a/usrsctplib/netinet/sctp_pcb.h b/usrsctplib/netinet/sctp_pcb.h index 34881863f..be4a3f2fa 100755 --- a/usrsctplib/netinet/sctp_pcb.h +++ b/usrsctplib/netinet/sctp_pcb.h @@ -115,6 +115,7 @@ struct sctp_ifa { uint32_t flags; uint32_t localifa_flags; uint32_t vrf_id; /* vrf_id of this addr (for deleting) */ + uint32_t ifa_mtu; uint8_t src_is_loop; uint8_t src_is_priv; uint8_t src_is_glob; @@ -609,6 +610,7 @@ int register_recv_cb (struct socket *, struct sctp_rcvinfo, int, void *)); int register_send_cb (struct socket *, uint32_t, int (*)(struct socket *, uint32_t)); int register_ulp_info (struct socket *, void *); +int sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa); #endif struct sctp_tcb { diff --git a/usrsctplib/netinet/sctp_structs.h b/usrsctplib/netinet/sctp_structs.h index f49f2c87a..d3a58b65e 100755 --- a/usrsctplib/netinet/sctp_structs.h +++ b/usrsctplib/netinet/sctp_structs.h @@ -447,6 +447,9 @@ struct sctp_nets { uint32_t flowid; uint8_t flowtype; #endif +#if defined(__Userspace__) + uint8_t got_max; +#endif }; diff --git a/usrsctplib/netinet/sctp_usrreq.c b/usrsctplib/netinet/sctp_usrreq.c index 8f2b40024..b39787b17 100755 --- a/usrsctplib/netinet/sctp_usrreq.c +++ b/usrsctplib/netinet/sctp_usrreq.c @@ -249,13 +249,32 @@ sctp_finish(void) #endif void -sctp_pathmtu_adjustment(struct sctp_tcb *stcb, uint16_t nxtsz) +sctp_pathmtu_adjustment(struct sctp_tcb *stcb, uint16_t nxtsz, struct sctp_nets * net) { struct sctp_tmit_chunk *chk; - uint16_t overhead; + uint16_t overhead, allow = 0; + struct sctp_nets *mnet; - /* Adjust that too */ - stcb->asoc.smallest_mtu = nxtsz; + if (stcb->asoc.smallest_mtu >= nxtsz) { + stcb->asoc.smallest_mtu = nxtsz; + } else { + if (stcb->asoc.numnets == 1) { + stcb->asoc.smallest_mtu = nxtsz; + } else { + TAILQ_FOREACH(mnet, &stcb->asoc.nets, sctp_next) { + if (mnet->mtu > stcb->asoc.smallest_mtu) { + allow++; + } else { + if (mnet == net) { + allow++; + } + } + } + if (stcb->asoc.numnets == allow) { + stcb->asoc.smallest_mtu = nxtsz; + } + } + } /* now off to subtract IP_DF flag if needed */ overhead = IP_HDR_SIZE + sizeof(struct sctphdr); if (sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.peer_auth_chunks)) { @@ -396,7 +415,7 @@ sctp_notify(struct sctp_inpcb *inp, } /* Update the association MTU */ if (stcb->asoc.smallest_mtu > next_mtu) { - sctp_pathmtu_adjustment(stcb, next_mtu); + sctp_pathmtu_adjustment(stcb, next_mtu, net); } /* Finally, start the PMTU timer if it was running before. */ if (timer_stopped) { @@ -6164,7 +6183,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, break; } if (net->mtu < stcb->asoc.smallest_mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } } if (paddrp->spp_flags & SPP_PMTUD_ENABLE) { @@ -6312,7 +6331,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, break; } if (net->mtu < stcb->asoc.smallest_mtu) { - sctp_pathmtu_adjustment(stcb, net->mtu); + sctp_pathmtu_adjustment(stcb, net->mtu, net); } } stcb->asoc.default_mtu = paddrp->spp_pathmtu; diff --git a/usrsctplib/netinet/sctp_var.h b/usrsctplib/netinet/sctp_var.h index bc4afe95f..000295989 100755 --- a/usrsctplib/netinet/sctp_var.h +++ b/usrsctplib/netinet/sctp_var.h @@ -417,7 +417,7 @@ int sctp_ctloutput __P((struct socket *, struct sockopt *)); void sctp_input_with_port __P((struct mbuf *, int, uint16_t)); void sctp_input __P((struct mbuf *, int)); #endif -void sctp_pathmtu_adjustment __P((struct sctp_tcb *, uint16_t)); +void sctp_pathmtu_adjustment __P((struct sctp_tcb *, uint16_t), struct sctp_nets *); #else #if defined(__APPLE__) && !defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) && !defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION) && !defined(APPLE_ELCAPITAN) void sctp_ctlinput(int, struct sockaddr *, void *, struct ifnet * SCTP_UNUSED); @@ -433,13 +433,13 @@ int sctp_input(struct mbuf **, int *, int); void sctp_input(struct mbuf *, int); #endif #endif -void sctp_pathmtu_adjustment(struct sctp_tcb *, uint16_t); +void sctp_pathmtu_adjustment(struct sctp_tcb *, uint16_t, struct sctp_nets *); #endif #else #if defined(__Panda__) void sctp_input(pakhandle_type i_pak); #elif defined(__Userspace__) -void sctp_pathmtu_adjustment(struct sctp_tcb *, uint16_t); +void sctp_pathmtu_adjustment(struct sctp_tcb *, uint16_t, struct sctp_nets *); #else void sctp_input(struct mbuf *,...); #endif diff --git a/usrsctplib/netinet6/sctp6_usrreq.c b/usrsctplib/netinet6/sctp6_usrreq.c index 5a57fd6e7..346f6eb12 100644 --- a/usrsctplib/netinet6/sctp6_usrreq.c +++ b/usrsctplib/netinet6/sctp6_usrreq.c @@ -410,7 +410,7 @@ sctp6_notify(struct sctp_inpcb *inp, } /* Update the association MTU */ if (stcb->asoc.smallest_mtu > next_mtu) { - sctp_pathmtu_adjustment(stcb, next_mtu); + sctp_pathmtu_adjustment(stcb, next_mtu, net); } /* Finally, start the PMTU timer if it was running before. */ if (timer_stopped) { From a96e4fe535b03cef8d5dd349ab50db2fa97f7d7d Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Mon, 4 Jun 2018 13:46:03 +0200 Subject: [PATCH 04/11] Add IPv6 support to tsctp --- programs/CMakeLists.txt | 1 + programs/tsctp6.c | 1001 ++++++++++++++++++++++++++++++ usrsctplib/netinet/sctp_output.c | 2 +- usrsctplib/netinet/sctp_pcb.c | 70 +-- usrsctplib/user_mbuf.h | 1 - 5 files changed, 1005 insertions(+), 70 deletions(-) create mode 100644 programs/tsctp6.c diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index ab4c7fda3..c687a09a4 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -83,6 +83,7 @@ set(check_programs test_libmgmt.c test_timer.c tsctp.c + tsctp6.c ) foreach (source_file ${check_programs}) diff --git a/programs/tsctp6.c b/programs/tsctp6.c new file mode 100644 index 000000000..9bfe27bfe --- /dev/null +++ b/programs/tsctp6.c @@ -0,0 +1,1001 @@ +/* + * Copyright (C) 2005-2013 Michael Tuexen + * Copyright (C) 2011-2013 Irene Ruengeler + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifdef _WIN32 +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef LINUX +#include +#endif +#include + +/* global for the send callback, but used in kernel version as well */ +static unsigned long number_of_messages; +static char *buffer; +static int length; +static union sock_union{ + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; +} remote_addr; +static int unordered; +uint32_t optval = 1; +struct socket *psock = NULL; + +static struct timeval start_time; +unsigned int runtime = 0; +static unsigned long messages = 0; +static unsigned long long first_length = 0; +static unsigned long long sum = 0; +static unsigned int use_cb = 0; + +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + + +char Usage[] = +"Usage: tsctp [options] [address]\n" +"Options:\n" +" -a set adaptation layer indication\n" +" -c use callback API\n" +" -E local UDP encapsulation port (default 9899)\n" +" -f fragmentation point\n" +" -l size of send/receive buffer\n" +" -L bind to local address\n" +" -n number of messages sent (0 means infinite)/received\n" +" -D turns Nagle off\n" +" -R socket recv buffer\n" +" -S socket send buffer\n" +" -T time to send messages\n" +" -u use unordered user messages\n" +" -U remote UDP encapsulation port\n" +" -v verbose\n" +" -V very verbose\n" +" -m enable path mtu discovery\n" +" -4 IPv4 only\n" +" -6 IPv6 only\n" +; + +#define DEFAULT_LENGTH 1024 +#define DEFAULT_NUMBER_OF_MESSAGES 1024 +#define DEFAULT_PORT 5001 +#define BUFFERSIZE (1<<16) +#define MAX_LOCAL_ADDR 10 + +static int verbose, very_verbose; +static unsigned int done; + +void stop_sender(int sig) +{ + done = 1; +} + +#ifdef _WIN32 +static void +gettimeofday(struct timeval *tv, void *ignore) +{ + struct timeb tb; + + ftime(&tb); + tv->tv_sec = (long)tb.time; + tv->tv_usec = tb.millitm * 1000; +} +#endif + +#ifdef _WIN32 +static DWORD WINAPI +#else +static void * +#endif +handle_connection(void *arg) +{ + ssize_t n; + char *buf; +#ifdef _WIN32 + HANDLE tid; +#else + pthread_t tid; +#endif + struct socket *conn_sock; + struct timeval time_start, time_now, time_diff; + double seconds; + unsigned long recv_calls = 0; + unsigned long notifications = 0; + int flags; + struct sockaddr addr; + socklen_t len; + union sctp_notification *snp; + struct sctp_paddr_change *spc; + struct timeval note_time; + unsigned int infotype; + struct sctp_recvv_rn rn; + socklen_t infolen = sizeof(struct sctp_recvv_rn); + + conn_sock = *(struct socket **)arg; +#ifdef _WIN32 + tid = GetCurrentThread(); +#else + tid = pthread_self(); + pthread_detach(tid); +#endif + + buf = malloc(BUFFERSIZE); + flags = 0; + len = (socklen_t)0; + infotype = 0; + memset(&rn, 0, sizeof(struct sctp_recvv_rn)); + n = usrsctp_recvv(conn_sock, (void*)buf, BUFFERSIZE, (struct sockaddr *) &addr, &len, + (void *)&rn, &infolen, &infotype, &flags); + + gettimeofday(&time_start, NULL); + while (n > 0) { + recv_calls++; + if (flags & MSG_NOTIFICATION) { + notifications++; + gettimeofday(¬e_time, NULL); + printf("notification arrived at %f\n", note_time.tv_sec+(double)note_time.tv_usec/1000000.0); + snp = (union sctp_notification *)buf; + if (snp->sn_header.sn_type==SCTP_PEER_ADDR_CHANGE) + { + spc = &snp->sn_paddr_change; + printf("SCTP_PEER_ADDR_CHANGE: state=%d, error=%d\n",spc->spc_state, spc->spc_error); + } + } else { + if (very_verbose) { + printf("Message received\n"); + } + sum += n; + if (flags & MSG_EOR) { + messages++; + if (first_length == 0) + first_length = sum; + } + } + flags = 0; + len = (socklen_t)sizeof(struct sockaddr); + infolen = sizeof(struct sctp_recvv_rn); + infotype = 0; + memset(&rn, 0, sizeof(struct sctp_recvv_rn)); + n = usrsctp_recvv(conn_sock, (void *) buf, BUFFERSIZE, (struct sockaddr *) &addr, &len, (void *)&rn, + &infolen, &infotype, &flags); + } + if (n < 0) + perror("usrsctp_recvv"); + gettimeofday(&time_now, NULL); + timersub(&time_now, &time_start, &time_diff); + seconds = time_diff.tv_sec + (double)time_diff.tv_usec/1000000.0; + printf("%llu, %lu, %lu, %lu, %llu, %f, %f\n", + first_length, messages, recv_calls, notifications, sum, seconds, (double)first_length * (double)messages / seconds); + fflush(stdout); + usrsctp_close(conn_sock); + free(buf); +#ifdef _WIN32 + return 0; +#else + return (NULL); +#endif +} + +static int +send_cb(struct socket *sock, uint32_t sb_free) { + struct sctp_sndinfo sndinfo; + + if ((messages == 0) & verbose) { + printf("Start sending "); + if (number_of_messages > 0) { + printf("%ld messages ", (long)number_of_messages); + } + if (runtime > 0) { + printf("for %u seconds ...", runtime); + } + printf("\n"); + fflush(stdout); + } + + sndinfo.snd_sid = 0; + sndinfo.snd_flags = 0; + if (unordered != 0) { + sndinfo.snd_flags |= SCTP_UNORDERED; + } + sndinfo.snd_ppid = 0; + sndinfo.snd_context = 0; + sndinfo.snd_assoc_id = 0; + + while (!done && ((number_of_messages == 0) || (messages < (number_of_messages - 1)))) { + char temp[INET6_ADDRSTRLEN]; + if (very_verbose) { + printf("Sending message number %lu.\n", messages + 1); + if (remote_addr.sa.sa_family == AF_INET) { + fprintf(stdout,"Send message to %s:%d\n", inet_ntop(AF_INET, &remote_addr.s4.sin_addr, temp, INET_ADDRSTRLEN), ntohs(remote_addr.s4.sin_port)); + } else { + fprintf(stdout,"Send message to %s:%d\n", inet_ntop(AF_INET6, &remote_addr.s6.sin6_addr, temp, INET6_ADDRSTRLEN), ntohs(remote_addr.s6.sin6_port)); + } + } + + if (usrsctp_sendv(psock, buffer, length, + (struct sockaddr *) &remote_addr, 1, + (void *)&sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, + 0) < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + perror("usrsctp_sendv (cb)"); + exit(1); + } else { + if (very_verbose){ + printf("EWOULDBLOCK or EAGAIN for message number %lu - will retry\n", messages + 1); + } + /* send until EWOULDBLOCK then exit callback. */ + return (1); + } + } + messages++; + } + if ((done == 1) || (messages == (number_of_messages - 1))) { + if (very_verbose) + printf("Sending final message number %lu.\n", messages + 1); + + sndinfo.snd_flags |= SCTP_EOF; + if (usrsctp_sendv(psock, buffer, length, (struct sockaddr *) &remote_addr, 1, + (void *)&sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, + 0) < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + perror("usrsctp_sendv (cb)"); + exit(1); + } else { + if (very_verbose){ + printf("EWOULDBLOCK or EAGAIN for final message number %lu - will retry\n", messages + 1); + } + /* send until EWOULDBLOCK then exit callback. */ + return (1); + } + } + messages++; + done = 2; + } + + return (1); +} + +static int +server_receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, + size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) +{ + struct timeval now, diff_time; + double seconds; + + if (data == NULL) { + gettimeofday(&now, NULL); + timersub(&now, &start_time, &diff_time); + seconds = diff_time.tv_sec + (double)diff_time.tv_usec/1000000.0; + printf("%llu, %lu, %llu, %f, %f\n", + first_length, messages, sum, seconds, (double)first_length * (double)messages / seconds); + usrsctp_close(sock); + first_length = 0; + sum = 0; + messages = 0; + return (1); + } + if (first_length == 0) { + first_length = (unsigned int)datalen; + gettimeofday(&start_time, NULL); + } + sum += datalen; + messages++; + + free(data); + return (1); +} + +static int +client_receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, + size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) +{ + free(data); + return (1); +} + +void +debug_printf(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vprintf(format, ap); + va_end(ap); +} + +int main(int argc, char **argv) +{ +#ifndef _WIN32 + int c; +#endif + socklen_t addr_len; + unsigned int nr_local_addr = 0; + struct sockaddr_storage local_addr[MAX_LOCAL_ADDR]; + char *local_addr_ptr = (char*) local_addr; + struct timeval time_start, time_now, time_diff; + int client; + uint16_t local_port, remote_port, port, local_udp_port, remote_udp_port, i; + int rcvbufsize=0, sndbufsize=0, myrcvbufsize, mysndbufsize; + socklen_t intlen; + double seconds; + double throughput; + int nodelay = 0; + struct sctp_assoc_value av; + struct sctp_udpencaps encaps; + struct sctp_sndinfo sndinfo; +#ifdef _WIN32 + HANDLE tid; +#else + pthread_t tid; +#endif + int fragpoint = 0; + struct sctp_setadaptation ind = {0}; +#ifdef _WIN32 + char *opt; + int optind; +#endif + int ipv4only = 0; + int ipv6only = 0; + + unordered = 0; + length = DEFAULT_LENGTH; + number_of_messages = DEFAULT_NUMBER_OF_MESSAGES; + port = DEFAULT_PORT; + remote_udp_port = 0; + local_udp_port = 9899; + verbose = 0; + very_verbose = 0; + + memset((void *) &remote_addr, 0, sizeof(remote_addr)); + +#ifndef _WIN32 + while ((c = getopt(argc, argv, "a:cp:l:E:f:L:n:R:S:T:uU:vVD46")) != -1) + switch(c) { + case 'a': + ind.ssb_adaptation_ind = atoi(optarg); + break; + case 'c': + use_cb = 1; + break; + case 'l': + length = atoi(optarg); + break; + case 'n': + number_of_messages = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 'E': + local_udp_port = atoi(optarg); + break; + case 'f': + fragpoint = atoi(optarg); + break; + case 'L': + if (nr_local_addr < MAX_LOCAL_ADDR) { + struct sockaddr_in *s4 = (struct sockaddr_in*) local_addr_ptr; + struct sockaddr_in6 *s6 = (struct sockaddr_in6*) local_addr_ptr; + + if (inet_pton(AF_INET6, optarg, &s6->sin6_addr)) { + s6->sin6_family = AF_INET6; +#ifdef HAVE_SIN6_LEN + s6->sin6_len = sizeof(struct sockaddr_in6); +#endif + local_addr_ptr += sizeof(struct sockaddr_in6); + nr_local_addr++; + } else { + if (inet_pton(AF_INET, optarg, &s4->sin_addr)) { + s4->sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + s4->sin_len = sizeof(struct sockaddr_in); +#endif + local_addr_ptr += sizeof(struct sockaddr_in); + nr_local_addr++; + } else { + printf("Invalid address\n"); + fprintf(stderr, "%s", Usage); + exit(1); + } + } + } + break; + case 'R': + rcvbufsize = atoi(optarg); + break; + case 'S': + sndbufsize = atoi(optarg); + break; + case 'T': + runtime = atoi(optarg); + number_of_messages = 0; + break; + case 'u': + unordered = 1; + break; + case 'U': + remote_udp_port = atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case 'V': + verbose = 1; + very_verbose = 1; + break; + case 'D': + nodelay = 1; + break; + case '4': + ipv4only = 1; + if (ipv6only) { + printf("IPv6 only already\n"); + exit(1); + } + break; + case '6': + ipv6only = 1; + if (ipv4only) { + printf("IPv4 only already\n"); + exit(1); + } + break; + default: + fprintf(stderr, "%s", Usage); + exit(1); + } +#else + for (optind = 1; optind < argc; optind++) { + if (argv[optind][0] == '-') { + switch (argv[optind][1]) { + case 'a': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + ind.ssb_adaptation_ind = atoi(opt); + break; + case 'c': + use_cb = 1; + break; + case 'l': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + length = atoi(opt); + break; + case 'p': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + port = atoi(opt); + break; + case 'n': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + number_of_messages = atoi(opt); + break; + case 'f': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + fragpoint = atoi(opt); + break; + case 'L': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + if (nr_local_addr < MAX_LOCAL_ADDR) { + struct sockaddr_in *s4 = (struct sockaddr_in*) local_addr_ptr; + struct sockaddr_in6 *s6 = (struct sockaddr_in6*) local_addr_ptr; + + if (inet_pton(AF_INET6, opt, &s6->sin6_addr)) { + s6->sin6_family = AF_INET6; +#ifdef HAVE_SIN6_LEN + s6->sin6_len = sizeof(struct sockaddr_in6); +#endif + local_addr_ptr += sizeof(struct sockaddr_in6); + nr_local_addr++; + } else { + if (inet_pton(AF_INET, opt, &s4->sin_addr)) { + s4->sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + s4->sin_len = sizeof(struct sockaddr_in); +#endif + local_addr_ptr += sizeof(struct sockaddr_in); + nr_local_addr++; + } else { + printf("Invalid address\n"); + fprintf(stderr, "%s", Usage); + exit(1); + } + } + } + break; + case 'U': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + remote_udp_port = atoi(opt); + break; + case 'E': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + local_udp_port = atoi(opt); + break; + case 'R': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + rcvbufsize = atoi(opt); + break; + case 'S': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + sndbufsize = atoi(opt); + break; + case 'T': + if (++optind >= argc) { + printf("%s", Usage); + exit(1); + } + opt = argv[optind]; + runtime = atoi(opt); + number_of_messages = 0; + break; + case 'u': + unordered = 1; + break; + case 'v': + verbose = 1; + break; + case 'V': + verbose = 1; + very_verbose = 1; + break; + case 'D': + nodelay = 1; + break; + case '4': + ipv4only = 1; + if (ipv6only) { + printf("IPv6 only already\n"); + exit(1); + } + break; + case '6': + ipv6only = 1; + if (ipv4only) { + printf("IPv4 only already\n"); + exit(1); + } + break; + default: + printf("%s", Usage); + exit(1); + } + } else { + break; + } + } +#endif + if (optind == argc) { + client = 0; + local_port = port; + remote_port = 0; + } else { + client = 1; + local_port = 0; + remote_port = port; + } + + if (nr_local_addr == 0) { + memset((void *) local_addr, 0, sizeof(local_addr)); + if (ipv4only) { + struct sockaddr_in *s4 = (struct sockaddr_in*) local_addr; + s4->sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + s4->sin_len = sizeof(struct sockaddr_in); +#endif + s4->sin_addr.s_addr = htonl(INADDR_ANY); + } else { + struct sockaddr_in6 *s6 = (struct sockaddr_in6*) local_addr; + s6->sin6_family = AF_INET6; +#ifdef HAVE_SIN6_LEN + s6->sin6_len = sizeof(struct sockaddr_in6); +#endif + s6->sin6_addr = in6addr_any; + } + nr_local_addr = 1; + } + local_addr_ptr = (char*) local_addr; + for (i = 0; i < nr_local_addr; i++) { + struct sockaddr_in *s4 = (struct sockaddr_in*) local_addr_ptr; + struct sockaddr_in6 *s6 = (struct sockaddr_in6*) local_addr_ptr; + + if (s4->sin_family == AF_INET) { + s4->sin_port = htons(local_port); + local_addr_ptr += sizeof(struct sockaddr_in); + if (ipv6only) { + printf("Can't use IPv4 address when IPv6 only\n"); + exit(1); + } + } else if (s6->sin6_family == AF_INET6) { + s6->sin6_port = htons(local_port); + local_addr_ptr += sizeof(struct sockaddr_in6); + if (ipv4only) { + printf("Can't use IPv6 address when IPv4 only\n"); + exit(1); + } + } + } + + usrsctp_init(local_udp_port, NULL, debug_printf); +#ifdef SCTP_DEBUG + usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL); +#endif + usrsctp_sysctl_set_sctp_blackhole(2); + usrsctp_sysctl_set_sctp_enable_sack_immediately(1); + + + if (client) { + if (use_cb) { + if (!(psock = usrsctp_socket((ipv4only ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_SCTP, client_receive_cb, send_cb, length, NULL))) { + perror("usersctp_socket"); + exit(1); + } + } else { + if (!(psock = usrsctp_socket((ipv4only ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_SCTP, NULL, NULL, 0, NULL))) { + perror("usersctp_socket"); + exit(1); + } + } + } else { + if (use_cb) { + if (!(psock = usrsctp_socket((ipv4only ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_SCTP, server_receive_cb, NULL, 0, NULL))) { + perror("usersctp_socket"); + exit(1); + } + } else { + if (!(psock = usrsctp_socket((ipv4only ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_SCTP, NULL, NULL, 0, NULL))) { + perror("usersctp_socket"); + exit(1); + } + } + } + + + + if (nr_local_addr > 0) { + if (usrsctp_bindx(psock, (struct sockaddr *)local_addr, nr_local_addr, SCTP_BINDX_ADD_ADDR) != 0) { + perror("usrsctp_bindx"); + exit(1); + } + } + + if (ind.ssb_adaptation_ind > 0) { + if (usrsctp_setsockopt(psock, IPPROTO_SCTP, SCTP_ADAPTATION_LAYER, (const void*)&ind, (socklen_t)sizeof(struct sctp_setadaptation)) < 0) { + perror("usrsctp_setsockopt"); + } + } + + if (!client) { + if (rcvbufsize) { + if (usrsctp_setsockopt(psock, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, sizeof(int)) < 0) { + perror("usrsctp_setsockopt: rcvbuf"); + } + } + if (verbose) { + intlen = sizeof(int); + if (usrsctp_getsockopt(psock, SOL_SOCKET, SO_RCVBUF, &myrcvbufsize, (socklen_t *)&intlen) < 0) { + perror("usrsctp_getsockopt: rcvbuf"); + } else { + fprintf(stdout,"Receive buffer size: %d.\n", myrcvbufsize); + } + } + + if (usrsctp_listen(psock, 1) < 0) { + perror("usrsctp_listen"); + exit(1); + } + + while (1) { + memset(&remote_addr, 0, sizeof(remote_addr)); + if (ipv4only) { + addr_len = sizeof(struct sockaddr_in); + } else { + addr_len = sizeof(struct sockaddr_in6); + } + + if (use_cb) { + struct socket *conn_sock; + + if ((conn_sock = usrsctp_accept(psock, (struct sockaddr *) &remote_addr, &addr_len))== NULL) { + perror("usrsctp_accept"); + continue; + } + } else { + struct socket **conn_sock; + + conn_sock = (struct socket **)malloc(sizeof(struct socket *)); + if ((*conn_sock = usrsctp_accept(psock, (struct sockaddr *)&remote_addr, &addr_len)) == NULL) { + perror("usrsctp_accept"); + continue; + } +#ifdef _WIN32 + tid = CreateThread(NULL, 0, &handle_connection, (void *)conn_sock, 0, NULL); +#else + pthread_create(&tid, NULL, &handle_connection, (void *)conn_sock); +#endif + } + if (verbose) { + char temp[INET6_ADDRSTRLEN]; + + if (remote_addr.sa.sa_family == AF_INET) { + fprintf(stdout,"Connection accepted from %s:%d\n", inet_ntop(AF_INET, &remote_addr.s4.sin_addr, temp, INET_ADDRSTRLEN), ntohs(remote_addr.s4.sin_port)); + } else { + fprintf(stdout,"Connection accepted from %s:%d\n", inet_ntop(AF_INET6, &remote_addr.s6.sin6_addr, temp, INET6_ADDRSTRLEN), ntohs(remote_addr.s6.sin6_port)); + } + } + } + } else { + if (inet_pton(AF_INET6, argv[optind], &remote_addr.s6.sin6_addr)) { + remote_addr.s6.sin6_family = AF_INET6; +#ifdef HAVE_SIN6_LEN + remote_addr.s6.sin6_len = sizeof(struct sockaddr_in6); +#endif + remote_addr.s6.sin6_port = htons(remote_port); + addr_len = sizeof(struct sockaddr_in6); + + if (ipv4only) { + printf("Can't use IPv6 address when IPv4 only\n"); + exit(1); + } + } else { + if (inet_pton(AF_INET, argv[optind], &remote_addr.s4.sin_addr)) + { + remote_addr.s4.sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + remote_addr.s4.sin_len = sizeof(struct sockaddr_in); +#endif + remote_addr.s4.sin_port = htons(remote_port); + addr_len = sizeof(struct sockaddr_in); + + if (ipv6only) { + printf("Can't use IPv4 address when IPv6 only\n"); + exit(1); + } + } else { + printf("Invalid address\n"); + fprintf(stderr, "%s", Usage); + exit(1); + } + } + + memset(&encaps, 0, sizeof(struct sctp_udpencaps)); + encaps.sue_address.ss_family = remote_addr.sa.sa_family; + encaps.sue_port = htons(remote_udp_port); + if (usrsctp_setsockopt(psock, IPPROTO_SCTP, SCTP_REMOTE_UDP_ENCAPS_PORT, (const void*)&encaps, (socklen_t)sizeof(struct sctp_udpencaps)) < 0) { + perror("usrsctp_setsockopt"); + } + + if (nodelay == 1) { + optval = 1; + } else { + optval = 0; + } + if (usrsctp_setsockopt(psock, IPPROTO_SCTP, SCTP_NODELAY, &optval, sizeof(int)) < 0) { + perror("usrsctp_setsockopt: nodelay"); + } + + if (fragpoint) { + av.assoc_id = 0; + av.assoc_value = fragpoint; + if (usrsctp_setsockopt(psock, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) { + perror("setsockopt: SCTP_MAXSEG"); + } + } + + if (sndbufsize) + if (usrsctp_setsockopt(psock, SOL_SOCKET, SO_SNDBUF, &sndbufsize, sizeof(int)) < 0) { + perror("usrsctp_setsockopt: sndbuf"); + } + + if (verbose) { + intlen = sizeof(int); + if (usrsctp_getsockopt(psock, SOL_SOCKET, SO_SNDBUF, &mysndbufsize, (socklen_t *)&intlen) < 0) { + perror("usrsctp_getsockopt: sndbuf"); + } else { + fprintf(stdout,"Send buffer size: %d.\n", mysndbufsize); + } + } + + buffer = malloc(length); + memset(buffer, 'A', length); + + if (usrsctp_connect(psock, (struct sockaddr *) &remote_addr, addr_len) == -1 ) { + perror("usrsctp_connect"); + exit(1); + } + + gettimeofday(&time_start, NULL); + if (verbose && !very_verbose) { + printf("Start sending %ld messages...", (long)number_of_messages); + fflush(stdout); + } + + done = 0; + + if (runtime > 0) { +#ifndef _WIN32 + signal(SIGALRM, stop_sender); + alarm(runtime); +#else + printf("You cannot set the runtime in Windows yet\n"); + exit(-1); +#endif + } + + if (use_cb) { + while (done < 2 && (messages < (number_of_messages - 1))) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + } else { + sndinfo.snd_sid = 0; + sndinfo.snd_flags = 0; + if (unordered != 0) { + sndinfo.snd_flags |= SCTP_UNORDERED; + } + sndinfo.snd_ppid = 0; + sndinfo.snd_context = 0; + sndinfo.snd_assoc_id = 0; + if (verbose) { + printf("Start sending "); + if (number_of_messages > 0) { + printf("%ld messages ", (long)number_of_messages); + } + if (runtime > 0) { + printf("for %u seconds ...", runtime); + } + printf("\n"); + fflush(stdout); + } + while (!done && ((number_of_messages == 0) || (messages < (number_of_messages - 1)))) { + char temp[INET6_ADDRSTRLEN]; + if (very_verbose) { + printf("Sending message number %lu.\n", messages + 1); + } + + if (remote_addr.sa.sa_family == AF_INET) { + fprintf(stdout,"Send message to %s:%d\n", inet_ntop(AF_INET, &remote_addr.s4.sin_addr, temp, INET_ADDRSTRLEN), ntohs(remote_addr.s4.sin_port)); + } else { + fprintf(stdout,"Send message to %s:%d\n", inet_ntop(AF_INET6, &remote_addr.s6.sin6_addr, temp, INET6_ADDRSTRLEN), ntohs(remote_addr.s6.sin6_port)); + } + + if (usrsctp_sendv(psock, buffer, length, (struct sockaddr *) &remote_addr, 1, + (void *)&sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, + 0) < 0) { + perror("usrsctp_sendv"); + exit(1); + } + messages++; + } + if (very_verbose) { + printf("Sending message number %lu.\n", messages + 1); + } + + sndinfo.snd_flags |= SCTP_EOF; + if (usrsctp_sendv(psock, buffer, length, (struct sockaddr *) &remote_addr, 1, + (void *)&sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, + 0) < 0) { + perror("usrsctp_sendv"); + exit(1); + } + messages++; + } + free (buffer); + + if (verbose) { + printf("Closing socket.\n"); + } + + usrsctp_close(psock); + gettimeofday(&time_now, NULL); + timersub(&time_now, &time_start, &time_diff); + seconds = time_diff.tv_sec + (double)time_diff.tv_usec/1000000; + printf("%s of %ld messages of length %u took %f seconds.\n", + "Sending", messages, length, seconds); + throughput = (double)messages * (double)length / seconds; + printf("Throughput was %f Byte/sec.\n", throughput); + } + + while (usrsctp_finish() != 0) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + return 0; +} diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 30425910e..e19a1b2a3 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4999,7 +4999,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } return (ret); } - #if defined(__Userspace__) case AF_CONN: { @@ -13099,6 +13098,7 @@ sctp_copy_resume(struct uio *uio, return (m); #elif defined(__FreeBSD__) && __FreeBSD_version > 602000 || defined(__Userspace__) struct mbuf *m; + m = m_uiotombuf(uio, M_WAITOK, max_send_len, 0, (M_PKTHDR | (user_marks_eor ? M_EOR : 0))); if (m == NULL) { diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index 5027257b3..cd1f92ab7 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -165,73 +165,7 @@ sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb) SCTP_INP_INFO_RUNLOCK(); } -#if 0 -#if defined (__Userspace_os_Windows) -int -sctp_get_mtu_from_addr(struct sockaddr *sa) -{ - int mtu = 0; -#if defined(INET) || defined(INET6) - int ret; - unsigned int i = 0; - DWORD Err, AdapterAddrsSize; - PIP_ADAPTER_ADDRESSES pAdapterAddrs, pAdapt; - ret = 0; - AdapterAddrsSize = 0; - pAdapterAddrs = NULL; - if ((Err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &AdapterAddrsSize)) != 0) { - if ((Err != ERROR_BUFFER_OVERFLOW) && (Err != ERROR_INSUFFICIENT_BUFFER)) { - SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() sizing failed with error code %d and AdapterAddrsSize = %d\n", Err, AdapterAddrsSize); - ret = -1; - goto cleanup; - } - } - - /* Allocate memory from sizing information */ - if ((pAdapterAddrs = (PIP_ADAPTER_ADDRESSES) GlobalAlloc(GPTR, AdapterAddrsSize)) == NULL) { - SCTPDBG(SCTP_DEBUG_USR, "Memory allocation error!\n"); - return -1; - } - /* Get actual adapter information */ - if ((Err = GetAdaptersAddresses(AF_INET, 0, NULL, pAdapterAddrs, &AdapterAddrsSize)) != ERROR_SUCCESS) { - SCTPDBG(SCTP_DEBUG_USR, "GetAdaptersV4Addresses() failed with error code %d\n", Err); - ret = -1; - goto cleanup; - } - for (pAdapt = pAdapterAddrs; pAdapt; pAdapt = pAdapt->Next) { - struct sockaddr *addr = (struct sockaddr *)pAdapt->FirstUnicastAddress->Address.lpSockaddr; - if (sa->sa_family != addr->sa_family) { - continue; - } - SCTPDBG(SCTP_DEBUG_OUTPUT3, "Compare to address: "); - SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT3, (struct sockaddr *)(addr)); - switch (sa->sa_family) { - case AF_INET: - if (memcmp(((const void *)&((struct sockaddr_in *)addr)->sin_addr), ((void *)&((struct sockaddr_in *)sa)->sin_addr), sizeof(struct in_addr)) == 0) { - mtu = pAdapt->Mtu; - goto cleanup; - } - break; - case AF_INET6: - if (memcmp(((const void *)&((struct sockaddr_in6 *)addr)->sin6_addr), ((void *)&((struct sockaddr_in6 *)sa)->sin6_addr), sizeof(struct in_addr)) == 0) { - mtu = pAdapt->Mtu; - goto cleanup; - } - break; - default: - printf("Address family not supported\n"); - } - } -cleanup: - if (pAdapterAddrs != NULL) { - GlobalFree(pAdapterAddrs); - } -#endif - return mtu; -} -#endif -#endif -//#if defined(__Userspace__) && !defined(__Userspace_os_NaCl) +#if defined(__Userspace__) int sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa) { @@ -258,7 +192,7 @@ sctp_get_mtu_from_addr(struct sctp_inpcb *inp, struct sockaddr *sa) #endif return 0; } -//#endif +#endif /*- * Addresses are added to VRF's (Virtual Router's). For BSD we diff --git a/usrsctplib/user_mbuf.h b/usrsctplib/user_mbuf.h index da949918c..9c5d0d7ef 100755 --- a/usrsctplib/user_mbuf.h +++ b/usrsctplib/user_mbuf.h @@ -60,7 +60,6 @@ struct mbuf *m_uiotombuf(struct uio *uio, int how, int len, int align, int flags u_int m_length(struct mbuf *m0, struct mbuf **last); struct mbuf *m_last(struct mbuf *m); - /* mbuf initialization function */ void mbuf_initialize(void *); From 41435ffc45af43624f11e666e1e2f5fbbc849d17 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Mon, 4 Jun 2018 14:42:47 +0200 Subject: [PATCH 05/11] Fix some compile errors --- usrsctplib/netinet/sctp_output.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 176fd3e9e..d39fa96c0 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4493,9 +4493,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } #else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); -#endif - - if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { + if (mtu > 0) { if (net->port) { mtu -= sizeof(struct udphdr); } @@ -4973,7 +4971,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } #else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); -#endif if (mtu > 0) { if (net->port) { mtu -= sizeof(struct udphdr); @@ -5003,6 +5000,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } return (ret); } +#endif /* INET6 */ #if defined(__Userspace__) case AF_CONN: { From b504eb05ec961eba4c97b6c1adb4bb645cc81686 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Tue, 5 Jun 2018 09:42:15 +0200 Subject: [PATCH 06/11] ICMPv6 --- usrsctplib/netinet/sctp_pcb.h | 3 + usrsctplib/netinet/sctp_usrreq.c | 9 + usrsctplib/netinet/sctputil.c | 26 +-- usrsctplib/netinet/sctputil.h | 9 +- usrsctplib/netinet6/sctp6_usrreq.c | 22 ++- usrsctplib/netinet6/sctp6_var.h | 13 ++ usrsctplib/user_recv_thread.c | 284 +++++++++++++++++++++++++++++ 7 files changed, 344 insertions(+), 22 deletions(-) diff --git a/usrsctplib/netinet/sctp_pcb.h b/usrsctplib/netinet/sctp_pcb.h index 3afe6fa43..32a97b5b9 100755 --- a/usrsctplib/netinet/sctp_pcb.h +++ b/usrsctplib/netinet/sctp_pcb.h @@ -338,12 +338,15 @@ struct sctp_base_info { #if defined(__Userspace_os_Windows) SOCKET userspace_rawsctp6; SOCKET userspace_udpsctp6; + SOCKET userspace_icmp6; #else int userspace_rawsctp6; int userspace_udpsctp6; + int userspace_icmp6; #endif userland_thread_t recvthreadraw6; userland_thread_t recvthreadudp6; + userland_thread_t recvthreadicmp6; #endif int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df); void (*debug_printf)(const char *format, ...); diff --git a/usrsctplib/netinet/sctp_usrreq.c b/usrsctplib/netinet/sctp_usrreq.c index 983f211cc..747cbb710 100755 --- a/usrsctplib/netinet/sctp_usrreq.c +++ b/usrsctplib/netinet/sctp_usrreq.c @@ -147,6 +147,7 @@ sctp_init(void) #ifdef INET6 SCTP_BASE_VAR(userspace_rawsctp6) = -1; SCTP_BASE_VAR(userspace_udpsctp6) = -1; + SCTP_BASE_VAR(userspace_icmp6) = -1; #endif SCTP_BASE_VAR(timer_thread_should_exit) = 0; SCTP_BASE_VAR(conn_output) = conn_output; @@ -228,6 +229,14 @@ sctp_finish(void) } #endif #ifdef INET6 + if (SCTP_BASE_VAR(userspace_icmp6) != -1) { +#if defined(__Userspace_os_Windows) + WaitForSingleObject(SCTP_BASE_VAR(recvthreadicmp6), INFINITE); + CloseHandle(SCTP_BASE_VAR(recvthreadicmp6)); +#else + pthread_join(SCTP_BASE_VAR(recvthreadicmp6), NULL); +#endif + } if (SCTP_BASE_VAR(userspace_rawsctp6) != -1) { #if defined(__Userspace_os_Windows) WaitForSingleObject(SCTP_BASE_VAR(recvthreadraw6), INFINITE); diff --git a/usrsctplib/netinet/sctputil.c b/usrsctplib/netinet/sctputil.c index 33e62efd6..ff6473683 100755 --- a/usrsctplib/netinet/sctputil.c +++ b/usrsctplib/netinet/sctputil.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD: head/sys/netinet/sctputil.c 334286 2018-05-28 13:31:47Z tuex #include #if !defined(__Userspace_os_Windows) #include +#include #endif #endif #if defined(__FreeBSD__) @@ -7904,9 +7905,9 @@ sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, void *vip, void *ct #endif #endif -#if defined(__FreeBSD__) && __FreeBSD_version >= 1100000 +#if defined(__Userspace__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1100000) #ifdef INET6 -static void +void sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx SCTP_UNUSED) { struct ip6ctlparam *ip6cp; @@ -7929,8 +7930,9 @@ sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx /* 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)) { + if (ip6cp->ip6c_m->m_pkthdr.len < (uint16_t) + (ip6cp->ip6c_off + sizeof(struct udphdr)+ offsetof(struct sctphdr, checksum))) { + SCTPDBG(SCTP_DEBUG_USR, "Packet too short!!\n"); return; } /* Copy out the UDP header. */ @@ -7995,12 +7997,12 @@ sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx return; } } else { -#if defined(__FreeBSD__) - 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)) { +#if defined(__FreeBSD__) || defined(__Userspace__) + if (ip6cp->ip6c_m->m_pkthdr.len >= (uint16_t) + (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 @@ -8044,7 +8046,7 @@ sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, void *d, void *ctx code = ICMP6_PARAMPROB_NEXTHEADER; } sctp6_notify(inp, stcb, net, type, code, - ntohl(ip6cp->ip6c_icmp6->icmp6_mtu)); + (uint16_t)ntohl(ip6cp->ip6c_icmp6->icmp6_mtu)); } else { #if defined(__FreeBSD__) && __FreeBSD_version < 500000 if (PRC_IS_REDIRECT(cmd) && (inp != NULL)) { @@ -8157,7 +8159,7 @@ 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, -#if __FreeBSD_version >= 1100000 +#if __FreeBSD_version >= 1100000 || defined(__Userspace__) sctp_recv_icmp6_tunneled_packet, #endif NULL))) { diff --git a/usrsctplib/netinet/sctputil.h b/usrsctplib/netinet/sctputil.h index cd99fe33c..c58c7cd19 100755 --- a/usrsctplib/netinet/sctputil.h +++ b/usrsctplib/netinet/sctputil.h @@ -121,9 +121,16 @@ void sctp_invoke_recv_callback(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_queued_to_read *, int); +#ifdef INET void sctp_recv_icmp_tunneled_packet(int cmd, struct sockaddr *sa, - void *vip, void *ctx SCTP_UNUSED); + void *vip, void *ctx SCTP_UNUSED); +#endif +#ifdef INET6 +void +sctp_recv_icmp6_tunneled_packet(int cmd, struct sockaddr *sa, + void *d, void *ctx SCTP_UNUSED); +#endif #endif void sctp_add_to_readq(struct sctp_inpcb *inp, diff --git a/usrsctplib/netinet6/sctp6_usrreq.c b/usrsctplib/netinet6/sctp6_usrreq.c index 346f6eb12..a657b7c34 100644 --- a/usrsctplib/netinet6/sctp6_usrreq.c +++ b/usrsctplib/netinet6/sctp6_usrreq.c @@ -337,6 +337,7 @@ sctp6_input(struct mbuf **i_pak, int *offp, int proto SCTP_UNUSED) return (sctp6_input_with_port(i_pak, offp, 0)); } #endif +#endif void sctp6_notify(struct sctp_inpcb *inp, @@ -350,7 +351,7 @@ sctp6_notify(struct sctp_inpcb *inp, struct socket *so; #endif int timer_stopped; - +printf("sctp6_notify\n"); switch (icmp6_type) { case ICMP6_DST_UNREACH: if ((icmp6_code == ICMP6_DST_UNREACH_NOROUTE) || @@ -390,6 +391,7 @@ sctp6_notify(struct sctp_inpcb *inp, } break; case ICMP6_PACKET_TOO_BIG: + printf("ICMP6_PACKET_TOO_BIG\n"); if (net->dest_state & SCTP_ADDR_NO_PMTUD) { SCTP_TCB_UNLOCK(stcb); break; @@ -437,7 +439,7 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) struct sctp_nets *net; struct sctphdr sh; struct sockaddr_in6 src, dst; - +printf("sctp6_ctlinput\n"); #ifdef HAVE_SA_LEN if (pktdst->sa_family != AF_INET6 || pktdst->sa_len != sizeof(struct sockaddr_in6)) { @@ -447,6 +449,7 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) return; } +#if !defined(__Userspace__) if ((unsigned)cmd >= PRC_NCMDS) { return; } @@ -455,6 +458,7 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) } else if (inet6ctlerrmap[cmd] == 0) { return; } +#endif /* If the parameter is from icmp6, decode it. */ if (d != NULL) { ip6cp = (struct ip6ctlparam *)d; @@ -529,11 +533,11 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) return; } } else { -#if defined(__FreeBSD__) - if (ip6cp->ip6c_m->m_pkthdr.len >= - ip6cp->ip6c_off + sizeof(struct sctphdr) + - sizeof(struct sctp_chunkhdr) + - offsetof(struct sctp_init, a_rwnd)) { +#if defined(__FreeBSD__) || defined(__Userspace__) + if (ip6cp->ip6c_m->m_pkthdr.len >= (uint16_t) + (ip6cp->ip6c_off + 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 @@ -567,10 +571,11 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) return; #endif } + printf("call sctp6_notify\n"); sctp6_notify(inp, stcb, net, ip6cp->ip6c_icmp6->icmp6_type, ip6cp->ip6c_icmp6->icmp6_code, - ntohl(ip6cp->ip6c_icmp6->icmp6_mtu)); + (uint16_t)ntohl(ip6cp->ip6c_icmp6->icmp6_mtu)); } else { #if defined(__FreeBSD__) && __FreeBSD_version < 500000 if (PRC_IS_REDIRECT(cmd) && (inp != NULL)) { @@ -590,7 +595,6 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) } } } -#endif /* * this routine can probably be collasped into the one in sctp_userreq.c diff --git a/usrsctplib/netinet6/sctp6_var.h b/usrsctplib/netinet6/sctp6_var.h index 3c4285d29..02ea4987a 100755 --- a/usrsctplib/netinet6/sctp6_var.h +++ b/usrsctplib/netinet6/sctp6_var.h @@ -46,6 +46,19 @@ extern void in6_sin6_2_sin(struct sockaddr_in *, struct sockaddr_in6 *); extern void in6_sin6_2_sin_in_sock(struct sockaddr *); extern void in6_sin_2_v4mapsin6(struct sockaddr_in *, struct sockaddr_in6 *); #endif +#ifdef INET6 +void sctp6_notify(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, + uint8_t, uint8_t, uint32_t); +void sctp6_ctlinput(int, struct sockaddr *, void *); +#if defined(__Userspace_os_Windows) +#define ICMP6_DST_UNREACH_NOROUTE 0 +#define ICMP6_DST_UNREACH_ADMIN 1 +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 +#define ICMP6_DST_UNREACH_ADDR 3 +#define ICMP6_DST_UNREACH_NOPORT 4 +#define ICMP6_PARAMPROB_NEXTHEADER 1 +#endif +#endif #endif #if defined(_KERNEL) diff --git a/usrsctplib/user_recv_thread.c b/usrsctplib/user_recv_thread.c index 1cff8fd4f..72b0dc3f8 100755 --- a/usrsctplib/user_recv_thread.c +++ b/usrsctplib/user_recv_thread.c @@ -58,7 +58,11 @@ #endif #endif #if defined(__Userspace__) +#ifdef INET6 +#include +#endif #if !defined(__Userspace_os_Windows) +#include #include #endif #if defined(__Userspace_os_Darwin) || defined(__Userspace_os_DragonFly) || defined(__Userspace_os_FreeBSD) @@ -305,6 +309,9 @@ recv_function_icmp(void *arg) int to_fill = MAXLEN_MBUF_CHAIN; /* iovlen is the size of each mbuf in the chain */ int i, n, ncounter = 0; +#if defined(__Userspace_os_Darwin) + int hlen; +#endif int iovlen = MCLBYTES; int want_ext = (iovlen > MLEN)? 1 : 0; int want_header = 0; @@ -434,6 +441,190 @@ recv_function_icmp(void *arg) } #endif +#ifdef INET6 +static void * +recv_function_icmp6(void *arg) +{ + struct mbuf **recvmbuf, *m; + struct ip6_hdr *inner_ip6; + struct icmp6_hdr *icmp6; + struct sockaddr_in6 icmpsrc; + struct sockaddr_in6 src, dst; +#if !defined(__Userspace_os_Windows) + struct msghdr msg; + struct iovec recv_iovec[MAXLEN_MBUF_CHAIN]; + struct cmsghdr *cmsgptr; + char cmsgbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))]; +#else + WSABUF recv_iovec[MAXLEN_MBUF_CHAIN]; + int nResult, m_ErrorCode; + DWORD flags; + struct sockaddr_in6 from; + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + LPFN_WSARECVMSG WSARecvMsg; + WSACMSGHDR *cmsgptr; + WSAMSG msg; + char ControlBuffer[1024]; + int fromlen; +#endif + /*Initially the entire set of mbufs is to be allocated. + to_fill indicates this amount. */ + int to_fill = MAXLEN_MBUF_CHAIN; + /* iovlen is the size of each mbuf in the chain */ + int i, n, ncounter = 0; + int iovlen = MCLBYTES; + int want_ext = (iovlen > MLEN)? 1 : 0; + int want_header = 0; + + sctp_userspace_set_threadname("IP6/ICMP6 rcv"); + + bzero((void *)&src, sizeof(struct sockaddr_in)); + bzero((void *)&dst, sizeof(struct sockaddr_in)); + + recvmbuf = malloc(sizeof(struct mbuf *) * MAXLEN_MBUF_CHAIN); + + while (1) { + for (i = 0; i < to_fill; i++) { + /* Not getting the packet header. Tests with chain of one run + as usual without having the packet header. + Have tried both sending and receiving + */ + recvmbuf[i] = sctp_get_mbuf_for_msg(iovlen, want_header, M_NOWAIT, want_ext, MT_DATA); +#if !defined(__Userspace_os_Windows) + recv_iovec[i].iov_base = (caddr_t)recvmbuf[i]->m_data; + recv_iovec[i].iov_len = iovlen; +#else + recv_iovec[i].buf = (caddr_t)recvmbuf[i]->m_data; + recv_iovec[i].len = iovlen; +#endif + } + to_fill = 0; +#if defined(__Userspace_os_Windows) + flags = 0; + ncounter = 0; + fromlen = sizeof(struct sockaddr_in6); + bzero((void *)&from, sizeof(struct sockaddr_in6)); + nResult = WSAIoctl(SCTP_BASE_VAR(userspace_icmp6), SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof WSARecvMsg_GUID, + &WSARecvMsg, sizeof WSARecvMsg, + &ncounter, NULL, NULL); + if (nResult == 0) { + msg.name = (void *)&src; + msg.namelen = sizeof(struct sockaddr_in6); + msg.lpBuffers = recv_iovec; + msg.dwBufferCount = MAXLEN_MBUF_CHAIN; + msg.Control.len = sizeof ControlBuffer; + msg.Control.buf = ControlBuffer; + msg.dwFlags = 0; + nResult = WSARecvMsg(SCTP_BASE_VAR(userspace_icmp6), &msg, &ncounter, NULL, NULL); + } + if (nResult != 0) { + m_ErrorCode = WSAGetLastError(); + if (m_ErrorCode == WSAETIMEDOUT) + continue; + if (m_ErrorCode == WSAENOTSOCK || m_ErrorCode == WSAEINTR) + break; + } + n = ncounter; +#else + bzero((void *)&msg, sizeof(struct msghdr)); + bzero((void *)&src, sizeof(struct sockaddr_in6)); + bzero((void *)&dst, sizeof(struct sockaddr_in6)); + bzero((void *)cmsgbuf, CMSG_SPACE(sizeof (struct in6_pktinfo))); + msg.msg_name = (void *)&src; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_iov = recv_iovec; + msg.msg_iovlen = MAXLEN_MBUF_CHAIN; + msg.msg_control = (void *)cmsgbuf; + msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof (struct in6_pktinfo)); + msg.msg_flags = 0; + + ncounter = n = recvmsg(SCTP_BASE_VAR(userspace_icmp6), &msg, 0); + if (n < 0) { + if (errno == EAGAIN) { + continue; + } else { + break; + } + } +#endif + SCTPDBG(SCTP_DEBUG_USR,"%d bytes received on ICMPv6 raw socket\n", n); + SCTP_HEADER_LEN(recvmbuf[0]) = n; + if (n <= iovlen) { + SCTP_BUF_LEN(recvmbuf[0]) = n; + (to_fill)++; + } else { + i = 0; + SCTP_BUF_LEN(recvmbuf[0]) = iovlen; + + ncounter -= iovlen; + (to_fill)++; + do { + recvmbuf[i]->m_next = recvmbuf[i+1]; + SCTP_BUF_LEN(recvmbuf[i]->m_next) = min(ncounter, iovlen); + i++; + ncounter -= iovlen; + (to_fill)++; + } while (ncounter > 0); + } + + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + if ((cmsgptr->cmsg_level == IPPROTO_IPV6) && (cmsgptr->cmsg_type == IPV6_PKTINFO)) { + struct in6_pktinfo * info; + + info = (struct in6_pktinfo *)CMSG_DATA(cmsgptr); + memcpy((void *)&dst.sin6_addr, (const void *) &(info->ipi6_addr), sizeof(struct in6_addr)); + break; + } + } + + /* SCTP does not allow broadcasts or multicasts */ + if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) { + m_freem(recvmbuf[0]); + continue; + } + + m = *recvmbuf; + + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t)); + if (icmp6->icmp6_type == ICMP6_DST_UNREACH || + icmp6->icmp6_type == ICMP6_TIME_EXCEEDED || + icmp6->icmp6_type == ICMP6_PARAM_PROB || + icmp6->icmp6_type == ICMP6_PACKET_TOO_BIG) { + struct ip6ctlparam *ip6cp; + ip6cp = malloc(sizeof(struct ip6ctlparam)); + bzero(ip6cp, sizeof(struct ip6ctlparam)); + bzero(&icmpsrc, sizeof(icmpsrc)); +#ifdef HAVE_SIN6_LEN + icmpsrc.sin6_len = sizeof(struct sockaddr_in6); +#endif + icmpsrc.sin6_family = AF_INET6; + inner_ip6 = (struct ip6_hdr *)(icmp6 + 1); + icmpsrc.sin6_addr = inner_ip6->ip6_src; + ip6cp->ip6c_m = m; + ip6cp->ip6c_icmp6 = icmp6; + ip6cp->ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); + ip6cp->ip6c_off = sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); + ip6cp->ip6c_finaldst = &dst.sin6_addr; + ip6cp->ip6c_src = &icmpsrc; + ip6cp->ip6c_nxt = ip6cp->ip6c_ip6->ip6_nxt; + + SCTPDBG(SCTP_DEBUG_USR,"ICMPv6: type=%d code=%d\n", icmp6->icmp6_type, icmp6->icmp6_code); + if (inner_ip6->ip6_nxt == IPPROTO_SCTP) { + sctp6_ctlinput(icmp6->icmp6_code, (struct sockaddr *)&icmpsrc, (void *)ip6cp); + } else if (inner_ip6->ip6_nxt == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr *)(ip6cp->ip6c_ip6 + 1); + unsigned int port = ntohs(udp->uh_sport); + if (port == SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) { + sctp_recv_icmp6_tunneled_packet(icmp6->icmp6_code, (struct sockaddr *)&icmpsrc, (void *)ip6cp, NULL); + } + } + } + } + return (NULL); +} +#endif + #ifdef INET static void * recv_function_raw(void *arg) @@ -1464,6 +1655,79 @@ recv_thread_init(void) } #endif #if defined(INET6) + if (SCTP_BASE_VAR(userspace_icmp6) == -1) { + if ((SCTP_BASE_VAR(userspace_icmp6) = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't create socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't create socket for SCTP/IPv6 (errno = %d).\n", errno); +#endif + } else { + /* complete setting up the raw SCTP socket */ +#if defined(IPV6_RECVPKTINFO) + if (setsockopt(SCTP_BASE_VAR(userspace_icmp6), IPPROTO_IPV6, IPV6_RECVPKTINFO, (const void *)&on, sizeof(on)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_RECVPKTINFO on socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_RECVPKTINFO on socket for SCTP/IPv6 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp6)); +#endif + SCTP_BASE_VAR(userspace_icmp6) = -1; + } else { +#else + if (setsockopt(SCTP_BASE_VAR(userspace_icmp6), IPPROTO_IPV6, IPV6_PKTINFO,(const void*)&on, sizeof(on)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_PKTINFO on socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_PKTINFO on socket for SCTP/IPv6 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp6)); +#endif + SCTP_BASE_VAR(userspace_icmp6) = -1; + } else { +#endif + if (setsockopt(SCTP_BASE_VAR(userspace_rawsctp6), IPPROTO_IPV6, IPV6_V6ONLY, (const void*)&on, (socklen_t)sizeof(on)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_V6ONLY on socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set IPV6_V6ONLY on socket for SCTP/IPv6 (errno = %d).\n", errno); +#endif + } + if (setsockopt(SCTP_BASE_VAR(userspace_icmp6), SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't set timeout on socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't set timeout on socket for SCTP/IPv6 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp6)); +#endif + SCTP_BASE_VAR(userspace_icmp6) = -1; + } else { + memset((void *)&addr_ipv6, 0, sizeof(struct sockaddr_in6)); +#ifdef HAVE_SIN6_LEN + addr_ipv6.sin6_len = sizeof(struct sockaddr_in6); +#endif + addr_ipv6.sin6_family = AF_INET6; + addr_ipv6.sin6_port = htons(0); + addr_ipv6.sin6_addr = in6addr_any; + if (bind(SCTP_BASE_VAR(userspace_icmp6), (const struct sockaddr *)&addr_ipv6, sizeof(struct sockaddr_in6)) < 0) { +#if defined(__Userspace_os_Windows) + SCTPDBG(SCTP_DEBUG_USR, "Can't bind socket for SCTP/IPv6 (errno = %d).\n", WSAGetLastError()); + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + SCTPDBG(SCTP_DEBUG_USR, "Can't bind socket for SCTP/IPv6 (errno = %d).\n", errno); + close(SCTP_BASE_VAR(userspace_icmp6)); +#endif + SCTP_BASE_VAR(userspace_icmp6) = -1; + } else { + setReceiveBufferSize(SCTP_BASE_VAR(userspace_icmp6), SB_RAW); /* 128K */ + setSendBufferSize(SCTP_BASE_VAR(userspace_icmp6), SB_RAW); /* 128K Is this setting net.inet.raw.maxdgram value? Should it be set to 64K? */ + } + } + } + } + } if (SCTP_BASE_VAR(userspace_rawsctp6) == -1) { if ((SCTP_BASE_VAR(userspace_rawsctp6) = socket(AF_INET6, SOCK_RAW, IPPROTO_SCTP)) == -1) { #if defined(__Userspace_os_Windows) @@ -1666,6 +1930,19 @@ recv_thread_init(void) } #endif #if defined(INET6) + if (SCTP_BASE_VAR(userspace_icmp6) != -1) { + int rc; + + if ((rc = sctp_userspace_thread_create(&SCTP_BASE_VAR(recvthreadicmp6), &recv_function_icmp6))) { + SCTPDBG(SCTP_DEBUG_USR, "Can't start SCTP/IPv6 recv thread (%d).\n", rc); +#if defined(__Userspace_os_Windows) + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + close(SCTP_BASE_VAR(userspace_icmp6)); +#endif + SCTP_BASE_VAR(userspace_icmp6) = -1; + } + } if (SCTP_BASE_VAR(userspace_rawsctp6) != -1) { int rc; @@ -1741,6 +2018,13 @@ recv_thread_destroy(void) closesocket(SCTP_BASE_VAR(userspace_udpsctp6)); #else close(SCTP_BASE_VAR(userspace_udpsctp6)); +#endif + } + if (SCTP_BASE_VAR(userspace_icmp6) != -1) { +#if defined(__Userspace_os_Windows) + closesocket(SCTP_BASE_VAR(userspace_icmp6)); +#else + close(SCTP_BASE_VAR(userspace_icmp6)); #endif } #endif From 3cba80a9332a9910c702cece41c99a84676c0175 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Wed, 6 Jun 2018 08:09:03 +0200 Subject: [PATCH 07/11] Some includes for Windows --- usrsctplib/user_ip6_var.h | 17 +++++++++++++++++ usrsctplib/user_recv_thread.c | 2 ++ 2 files changed, 19 insertions(+) diff --git a/usrsctplib/user_ip6_var.h b/usrsctplib/user_ip6_var.h index f5e4a60e4..9ce446e73 100755 --- a/usrsctplib/user_ip6_var.h +++ b/usrsctplib/user_ip6_var.h @@ -60,6 +60,23 @@ #ifndef _USER_IP6_VAR_H_ #define _USER_IP6_VAR_H_ +#include + +#if !defined(__Userspace_os_FreeBSD) +/* from netinet6/ip6protosw.h */ +struct ip6ctlparam { + struct mbuf *ip6c_m; /* start of mbuf chain */ + struct icmp6_hdr *ip6c_icmp6; /* icmp6 header of target packet */ + struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ + int ip6c_off; /* offset of the target proto header */ + struct sockaddr_in6 *ip6c_src; /* srcaddr w/ additional info */ + struct sockaddr_in6 *ip6c_dst; /* (final) dstaddr w/ additional info */ + struct in6_addr *ip6c_finaldst; /* final destination address */ + void *ip6c_cmdarg; /* control command dependent data */ + u_int8_t ip6c_nxt; /* final next header field */ +}; +#endif + #if defined(__Userspace_os_Windows) struct ip6_hdr { union { diff --git a/usrsctplib/user_recv_thread.c b/usrsctplib/user_recv_thread.c index 72b0dc3f8..089d2a51f 100755 --- a/usrsctplib/user_recv_thread.c +++ b/usrsctplib/user_recv_thread.c @@ -40,6 +40,8 @@ #else #include #endif +#else +#include #endif #include #include From c092bd9b5ac062fe77059a4fb4b9ad539dc78e9c Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Fri, 8 Jun 2018 09:58:20 +0200 Subject: [PATCH 08/11] Add functions for PLPMTUD support. --- usrsctplib/netinet/sctp_auth.c | 2 +- usrsctplib/netinet/sctp_auth.h | 3 + usrsctplib/netinet/sctp_constants.h | 15 ++ usrsctplib/netinet/sctp_header.h | 7 +- usrsctplib/netinet/sctp_output.c | 6 +- usrsctplib/netinet/sctp_output.h | 16 ++ usrsctplib/netinet/sctp_pcb.h | 1 + usrsctplib/netinet/sctp_structs.h | 6 + usrsctplib/netinet/sctputil.c | 288 ++++++++++++++++++++++++++++ usrsctplib/netinet/sctputil.h | 3 + 10 files changed, 342 insertions(+), 5 deletions(-) diff --git a/usrsctplib/netinet/sctp_auth.c b/usrsctplib/netinet/sctp_auth.c index 5e5813b3f..6fd95656f 100755 --- a/usrsctplib/netinet/sctp_auth.c +++ b/usrsctplib/netinet/sctp_auth.c @@ -1618,7 +1618,7 @@ sctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset, } -static void +void sctp_zero_m(struct mbuf *m, uint32_t m_offset, uint32_t size) { struct mbuf *m_tmp; diff --git a/usrsctplib/netinet/sctp_auth.h b/usrsctplib/netinet/sctp_auth.h index c1f82d882..93d167c6f 100755 --- a/usrsctplib/netinet/sctp_auth.h +++ b/usrsctplib/netinet/sctp_auth.h @@ -210,6 +210,9 @@ extern int sctp_validate_init_auth_params(struct mbuf *m, int offset, extern void sctp_initialize_auth_params(struct sctp_inpcb *inp, struct sctp_tcb *stcb); +void +sctp_zero_m(struct mbuf *m, uint32_t m_offset, uint32_t size); + /* test functions */ #ifdef SCTP_HMAC_TEST extern void sctp_test_hmac_sha1(void); diff --git a/usrsctplib/netinet/sctp_constants.h b/usrsctplib/netinet/sctp_constants.h index f9a87a47b..394bd38cb 100755 --- a/usrsctplib/netinet/sctp_constants.h +++ b/usrsctplib/netinet/sctp_constants.h @@ -724,6 +724,21 @@ extern void getwintimeofday(struct timeval *tv); #define SCTP_NUMBER_OF_SECRETS 8 /* or 8 * 4 = 32 octets */ #define SCTP_SECRET_SIZE 32 /* number of octets in a 256 bits */ +/* Probing states */ +#define SCTP_PROBE_NONE 0 +#define SCTP_PROBE_ERROR 1 +#define SCTP_PROBE_BASE 2 +#define SCTP_PROBE_SEARCH_UP 3 +#define SCTP_PROBE_SEARCH_DOWN 4 +#define SCTP_PROBE_DONE 5 + +#define SCTP_PROBE_MTU_V4_BASE 1200 +#define SCTP_PROBE_MTU_V6_BASE 1280 + +#define SCTP_PROBE_MAX_PROBES 2 +#define SCTP_PROBE_MIN 76 /* Size of a HEARTBEAT Chunk with HB Info */ + +#define SCTP_PROBE_UP 1 /* * SCTP upper layer notifications diff --git a/usrsctplib/netinet/sctp_header.h b/usrsctplib/netinet/sctp_header.h index 6035d9cbd..06627028f 100755 --- a/usrsctplib/netinet/sctp_header.h +++ b/usrsctplib/netinet/sctp_header.h @@ -106,7 +106,7 @@ struct sctp_heartbeat_info_param { uint8_t addr_family; uint8_t addr_len; /* make sure that this structure is 4 byte aligned */ - uint8_t padding[2]; + uint16_t probe_mtu; char address[SCTP_ADDRMAX]; } SCTP_PACKED; @@ -337,6 +337,11 @@ struct sctp_shutdown_ack_chunk { struct sctp_chunkhdr ch; } SCTP_PACKED; +/* Padding Chunk (PAD) */ +struct sctp_pad_chunk { + struct sctp_chunkhdr ch; + /* padding data follow */ +} SCTP_PACKED; /* Operation Error (ERROR) */ struct sctp_error_chunk { diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index d39fa96c0..6e4a5ca51 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4036,7 +4036,7 @@ sctp_get_ect(struct sctp_tcb *stcb) } #if defined(INET) || defined(INET6) -static void +void sctp_handle_no_route(struct sctp_tcb *stcb, struct sctp_nets *net, int so_locked) @@ -11612,14 +11612,14 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb, } #if defined(__FreeBSD__) -static void +void sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, uint32_t vtag, uint8_t type, struct mbuf *cause, uint8_t mflowtype, uint32_t mflowid, uint16_t fibnum, uint32_t vrf_id, uint16_t port) #else -static void +void sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, uint32_t vtag, uint8_t type, struct mbuf *cause, diff --git a/usrsctplib/netinet/sctp_output.h b/usrsctplib/netinet/sctp_output.h index 597ddc3bf..c5c607851 100755 --- a/usrsctplib/netinet/sctp_output.h +++ b/usrsctplib/netinet/sctp_output.h @@ -84,6 +84,13 @@ void sctp_send_initiate(struct sctp_inpcb *, struct sctp_tcb *, int #endif ); +#if defined(INET) || defined(INET6) +void +sctp_handle_no_route(struct sctp_tcb *stcb, + struct sctp_nets *net, + int so_locked); +#endif + void sctp_send_initiate_ack(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, struct mbuf *, @@ -230,6 +237,15 @@ sctp_send_abort(struct mbuf *, int, struct sockaddr *, struct sockaddr *, #endif uint32_t, uint16_t); +void +sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, + struct sctphdr *sh, uint32_t vtag, + uint8_t type, struct mbuf *cause, +#if defined(__FreeBSD__) + uint8_t mflowtype, uint32_t mflowid, uint16_t fibnum, +#endif + uint32_t vrf_id, uint16_t port); + void sctp_send_operr_to(struct sockaddr *, struct sockaddr *, struct sctphdr *, uint32_t, struct mbuf *, #if defined(__FreeBSD__) diff --git a/usrsctplib/netinet/sctp_pcb.h b/usrsctplib/netinet/sctp_pcb.h index 32a97b5b9..52548856f 100755 --- a/usrsctplib/netinet/sctp_pcb.h +++ b/usrsctplib/netinet/sctp_pcb.h @@ -507,6 +507,7 @@ struct sctp_inpcb { uint8_t reconfig_supported; uint8_t nrsack_supported; uint8_t pktdrop_supported; + uint8_t plpmtud_supported; struct sctp_nonpad_sndrcvinfo def_send; /*- * These three are here for the sosend_dgram diff --git a/usrsctplib/netinet/sctp_structs.h b/usrsctplib/netinet/sctp_structs.h index d3a58b65e..d569cc4a2 100755 --- a/usrsctplib/netinet/sctp_structs.h +++ b/usrsctplib/netinet/sctp_structs.h @@ -450,6 +450,12 @@ struct sctp_nets { #if defined(__Userspace__) uint8_t got_max; #endif + uint32_t probe_mtu; /* actual mtu probing size */ + uint32_t probed_mtu; /* mtu that has last been probed */ + uint32_t max_mtu; + uint16_t probe_counts; /* Counter for the probing attempts */ + uint16_t probing_state; + uint8_t mtu_probing; /* Flag to indicate whether mtu_probing should be processed */ }; diff --git a/usrsctplib/netinet/sctputil.c b/usrsctplib/netinet/sctputil.c index ff6473683..2a07a5d5d 100755 --- a/usrsctplib/netinet/sctputil.c +++ b/usrsctplib/netinet/sctputil.c @@ -8269,3 +8269,291 @@ sctp_hc_get_mtu(union sctp_sockstore *addr, uint16_t fibnum) return ((uint32_t)tcp_hc_getmtu(&inc)); } #endif + +static struct mbuf * +sctp_make_hb(struct sctp_tcb *stcb, struct sctp_nets *net,int so_locked +#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) + SCTP_UNUSED +#endif +) +{ + struct sctp_heartbeat_chunk *hb; + struct timeval now; + struct mbuf *chk; + uint16_t send_size; + + SCTP_TCB_LOCK_ASSERT(stcb); + if (net == NULL) { + return NULL; + } + (void)SCTP_GETTIME_TIMEVAL(&now); + switch (net->ro._l_addr.sa.sa_family) { +#ifdef INET + case AF_INET: + break; +#endif +#ifdef INET6 + case AF_INET6: + break; +#endif +#if defined(__Userspace__) + case AF_CONN: + break; +#endif + default: + return NULL; + } + send_size = sizeof(struct sctp_heartbeat_chunk); + + chk = sctp_get_mbuf_for_msg(send_size, 0, M_NOWAIT, 1, MT_HEADER); + if (chk == NULL) { + return NULL; + } + SCTP_BUF_RESV_UF(chk, SCTP_MIN_OVERHEAD); + SCTP_BUF_LEN(chk) = send_size; + /* Now we have a mbuf that we can fill in with the details */ + hb = mtod(chk, struct sctp_heartbeat_chunk *); + memset(hb, 0, send_size); + /* fill out chunk header */ + hb->ch.chunk_type = SCTP_HEARTBEAT_REQUEST; + hb->ch.chunk_flags = 0; + hb->ch.chunk_length = htons(send_size); + /* Fill out hb parameter */ + hb->heartbeat.hb_info.ph.param_type = htons(SCTP_HEARTBEAT_INFO); + hb->heartbeat.hb_info.ph.param_length = htons(sizeof(struct sctp_heartbeat_info_param)); + hb->heartbeat.hb_info.time_value_1 = now.tv_sec; + hb->heartbeat.hb_info.time_value_2 = now.tv_usec; + /* Did our user request this one, put it in */ + hb->heartbeat.hb_info.addr_family = (uint8_t)net->ro._l_addr.sa.sa_family; +#ifdef HAVE_SA_LEN + hb->heartbeat.hb_info.addr_len = net->ro._l_addr.sa.sa_len; +#else + switch (net->ro._l_addr.sa.sa_family) { +#ifdef INET + case AF_INET: + hb->heartbeat.hb_info.addr_len = sizeof(struct sockaddr_in); + break; +#endif +#ifdef INET6 + case AF_INET6: + hb->heartbeat.hb_info.addr_len = sizeof(struct sockaddr_in6); + break; +#endif +#if defined(__Userspace__) + case AF_CONN: + hb->heartbeat.hb_info.addr_len = sizeof(struct sockaddr_conn); + break; +#endif + default: + hb->heartbeat.hb_info.addr_len = 0; + break; + } +#endif + if (net->dest_state & SCTP_ADDR_UNCONFIRMED) { + /* + * we only take from the entropy pool if the address is not + * confirmed. + */ + net->heartbeat_random1 = hb->heartbeat.hb_info.random_value1 = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep); + net->heartbeat_random2 = hb->heartbeat.hb_info.random_value2 = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep); + } else { + net->heartbeat_random1 = hb->heartbeat.hb_info.random_value1 = 0; + net->heartbeat_random2 = hb->heartbeat.hb_info.random_value2 = 0; + } + switch (net->ro._l_addr.sa.sa_family) { +#ifdef INET + case AF_INET: + memcpy(hb->heartbeat.hb_info.address, + &net->ro._l_addr.sin.sin_addr, + sizeof(net->ro._l_addr.sin.sin_addr)); + break; +#endif +#ifdef INET6 + case AF_INET6: + memcpy(hb->heartbeat.hb_info.address, + &net->ro._l_addr.sin6.sin6_addr, + sizeof(net->ro._l_addr.sin6.sin6_addr)); + break; +#endif +#if defined(__Userspace__) + case AF_CONN: + memcpy(hb->heartbeat.hb_info.address, + &net->ro._l_addr.sconn.sconn_addr, + sizeof(net->ro._l_addr.sconn.sconn_addr)); + break; +#endif + default: + if (chk) { + sctp_m_freem(chk); + chk = NULL; + } + return NULL; + } + if (stcb->sctp_ep->plpmtud_supported && net->mtu_probing) { + hb->heartbeat.hb_info.probe_mtu = net->probe_mtu; + } else { + hb->heartbeat.hb_info.probe_mtu = 0; + } + net->hb_responded = 0; + return chk; +} + +static struct mbuf * +sctp_make_pad(struct sctp_tcb *stcb, struct sctp_nets *net, uint16_t pad_size) +{ + struct sctp_pad_chunk *pad; + struct mbuf *chk, *tmp_chk; + int size; + + SCTP_TCB_LOCK_ASSERT(stcb); + KASSERT(pad_size >= 4, ("%s: padsize %u too small", __FUNCTION__, pad_size)); + KASSERT(pad_size % 4 == 0, ("%s: padsize %u not aligned", __FUNCTION__, pad_size)); + KASSERT(net->mtu_probing != 0, ("%s: mtu_probing not enabled", __FUNCTION__)); + if (net == NULL) { + return NULL; + } + chk = sctp_get_mbuf_for_msg(pad_size, 0, M_NOWAIT, (pad_size < 2048)?1:0, MT_DATA); + if (chk == NULL) { + /* no mbufs */ + return NULL; + } + tmp_chk = chk; + size = pad_size; + while (tmp_chk != NULL && size > 0) { +#if __FreeBSD_version > 1100052 + if (size < SCTP_BUF_SIZE(tmp_chk)) { + SCTP_BUF_LEN(tmp_chk) = size; + size = 0; + } else { + SCTP_BUF_LEN(tmp_chk) = SCTP_BUF_SIZE(tmp_chk); + size -= SCTP_BUF_SIZE(tmp_chk); + } +#else + if (SCTP_BUF_IS_EXTENDED(tmp_chk)) { + if (size < (uint16_t)SCTP_BUF_EXTEND_SIZE(tmp_chk)) { + SCTP_BUF_LEN(tmp_chk) = size; + } else { + SCTP_BUF_LEN(tmp_chk) = SCTP_BUF_EXTEND_SIZE(tmp_chk); + } + size -= SCTP_BUF_LEN(tmp_chk); + } else { + if (size > MLEN) { + SCTP_BUF_LEN(tmp_chk) = MLEN; + size -= MLEN; + } else { + SCTP_BUF_LEN(tmp_chk) = size; + size = 0; + } + } +#endif + tmp_chk = SCTP_BUF_NEXT(tmp_chk); + } + + sctp_zero_m(chk, 0, pad_size); + pad = mtod(chk, struct sctp_pad_chunk *); + pad->ch.chunk_type = SCTP_PAD_CHUNK; + pad->ch.chunk_flags = 0; + pad->ch.chunk_length = htons(pad_size); + + net->hb_responded = 0; + return chk; +} + + +void +sctp_send_a_probe(struct sctp_inpcb *inp, + struct sctp_tcb *stcb, + struct sctp_nets *net) +{ + int ovh, sum_probe_chunks; + uint16_t send_size; + struct mbuf *hb, *pad, *m; + uint32_t vrf_id; + struct sctphdr *sh; + sctp_route_t *ro; + + if (!net->mtu_probing && net->probing_state > SCTP_PROBE_NONE) { + net->mtu_probing = 1; + net->probe_counts = 0; + } + if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ovh = SCTP_MIN_OVERHEAD; + } else { + ovh = SCTP_MIN_V4_OVERHEAD; + } +#if defined(__FreeBSD__) +#if defined(INET) || defined(INET6) + if (net->port) { + net->probe_mtu -= sizeof(struct udphdr); + } +#endif +#endif + + hb = sctp_make_hb(stcb, net, SCTP_SO_NOT_LOCKED); + if (hb == NULL) { + return; + } + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTPUTIL + SCTP_LOC_11); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + + sum_probe_chunks = net->probe_mtu - ovh - sizeof(struct sctp_heartbeat_info_param) - sizeof(struct sctp_chunkhdr); + send_size = sum_probe_chunks; + pad = sctp_make_pad(stcb, net, send_size); + if (pad == NULL) { + return; + } + + SCTP_BUF_NEXT(hb) = pad; +#if defined(INET) || defined(INET6) + if (stcb) { + vrf_id = stcb->asoc.vrf_id; + } else { + vrf_id = inp->def_vrf_id; + } +#endif + + m = sctp_get_mbuf_for_msg(sizeof(struct sctphdr), 1, M_NOWAIT, 1, MT_DATA); + sh = mtod(m, struct sctphdr *); + sh->src_port = stcb->rport; + sh->dest_port = inp->sctp_lport; + sh->v_tag = stcb->asoc.peer_vtag; + ro = (sctp_route_t *)&net->ro; + if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & (SCTP_BEING_DELETED|SCTP_ADDR_IFA_UNUSEABLE))) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + if (ro->ro_rt) { + RTFREE(ro->ro_rt); + ro->ro_rt = NULL; + } + } + if (net->src_addr_selected == 0) { + /* Cache the source address */ + net->ro._s_addr = sctp_source_address_selection(inp,stcb, + ro, net, 0, vrf_id); + net->src_addr_selected = 1; + } + if (net->ro._s_addr == NULL) { + /* No route to host */ + net->src_addr_selected = 0; + sctp_handle_no_route(stcb, net, SCTP_SO_NOT_LOCKED); + SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EHOSTUNREACH); + sctp_m_freem(m); + return; + } + sctp_send_resp_msg((struct sockaddr *)&net->ro._l_addr, + (struct sockaddr *)&net->ro._s_addr->address, + sh, stcb->asoc.peer_vtag, + SCTP_PAD_CHUNK, hb, +#if defined(__FreeBSD__) + net->flowtype, net->flowid, inp->fibnum, +#endif + vrf_id, net->port); + sctp_m_freem(m); + if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { + sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net, + SCTP_FROM_SCTPUTIL + SCTP_LOC_12); + } + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net); +} + diff --git a/usrsctplib/netinet/sctputil.h b/usrsctplib/netinet/sctputil.h index c58c7cd19..651253554 100755 --- a/usrsctplib/netinet/sctputil.h +++ b/usrsctplib/netinet/sctputil.h @@ -284,6 +284,9 @@ int sctp_cmpaddr(struct sockaddr *, struct sockaddr *); void sctp_print_address(struct sockaddr *); +void +sctp_send_a_probe(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net); + int sctp_release_pr_sctp_chunk(struct sctp_tcb *, struct sctp_tmit_chunk *, uint8_t, int From 4b2a26577a451817131748962596735939c0f4e1 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Mon, 11 Jun 2018 09:43:09 +0200 Subject: [PATCH 09/11] Add plpmtud as sysctl variable --- programs/tsctp6.c | 14 +++++++++++--- usrsctplib/netinet/sctp_input.c | 1 + usrsctplib/netinet/sctp_pcb.c | 1 + usrsctplib/netinet/sctp_peeloff.c | 1 + usrsctplib/netinet/sctp_structs.h | 1 + usrsctplib/netinet/sctp_sysctl.c | 7 +++++++ usrsctplib/netinet/sctp_sysctl.h | 7 +++++++ usrsctplib/netinet/sctputil.c | 1 + usrsctplib/user_socket.c | 2 ++ usrsctplib/usrsctp.h | 1 + 10 files changed, 33 insertions(+), 3 deletions(-) diff --git a/programs/tsctp6.c b/programs/tsctp6.c index 9bfe27bfe..a2cb99070 100644 --- a/programs/tsctp6.c +++ b/programs/tsctp6.c @@ -374,7 +374,7 @@ int main(int argc, char **argv) socklen_t intlen; double seconds; double throughput; - int nodelay = 0; + int nodelay = 0, enable_pmtu = 0;; struct sctp_assoc_value av; struct sctp_udpencaps encaps; struct sctp_sndinfo sndinfo; @@ -404,7 +404,7 @@ int main(int argc, char **argv) memset((void *) &remote_addr, 0, sizeof(remote_addr)); #ifndef _WIN32 - while ((c = getopt(argc, argv, "a:cp:l:E:f:L:n:R:S:T:uU:vVD46")) != -1) + while ((c = getopt(argc, argv, "a:cp:l:E:f:L:n:R:S:T:uU:vVDm46")) != -1) switch(c) { case 'a': ind.ssb_adaptation_ind = atoi(optarg); @@ -415,6 +415,9 @@ int main(int argc, char **argv) case 'l': length = atoi(optarg); break; + case 'm': + enable_pmtu = 1; + break; case 'n': number_of_messages = atoi(optarg); break; @@ -633,6 +636,9 @@ int main(int argc, char **argv) case 'D': nodelay = 1; break; + case 'm': + enable_pmtu = 1; + break; case '4': ipv4only = 1; if (ipv6only) { @@ -713,7 +719,9 @@ int main(int argc, char **argv) #endif usrsctp_sysctl_set_sctp_blackhole(2); usrsctp_sysctl_set_sctp_enable_sack_immediately(1); - + if (enable_pmtu == 1) { + usrsctp_sysctl_set_sctp_plpmtud_enable(1); + } if (client) { if (use_cb) { diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c index 120eabf4d..f2afd5425 100755 --- a/usrsctplib/netinet/sctp_input.c +++ b/usrsctplib/netinet/sctp_input.c @@ -3034,6 +3034,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, inp->reconfig_supported = (*inp_p)->reconfig_supported; inp->nrsack_supported = (*inp_p)->nrsack_supported; inp->pktdrop_supported = (*inp_p)->pktdrop_supported; + inp->plpmtud_supported = (*inp_p)->plpmtud_supported; inp->partial_delivery_point = (*inp_p)->partial_delivery_point; inp->sctp_context = (*inp_p)->sctp_context; inp->local_strreset_support = (*inp_p)->local_strreset_support; diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index 9eedb7467..51c50f7b8 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -2865,6 +2865,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) inp->reconfig_supported = (uint8_t)SCTP_BASE_SYSCTL(sctp_reconfig_enable); inp->nrsack_supported = (uint8_t)SCTP_BASE_SYSCTL(sctp_nrsack_enable); inp->pktdrop_supported = (uint8_t)SCTP_BASE_SYSCTL(sctp_pktdrop_enable); + inp->plpmtud_supported = (uint8_t) SCTP_BASE_SYSCTL(sctp_plpmtud_enable); inp->idata_supported = 0; #if defined(__FreeBSD__) diff --git a/usrsctplib/netinet/sctp_peeloff.c b/usrsctplib/netinet/sctp_peeloff.c index 391b4f544..89182ff4f 100755 --- a/usrsctplib/netinet/sctp_peeloff.c +++ b/usrsctplib/netinet/sctp_peeloff.c @@ -134,6 +134,7 @@ sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id) n_inp->reconfig_supported = inp->reconfig_supported; n_inp->nrsack_supported = inp->nrsack_supported; n_inp->pktdrop_supported = inp->pktdrop_supported; + n_inp->plpmtud_supported = inp->plpmtud_supported; n_inp->partial_delivery_point = inp->partial_delivery_point; n_inp->sctp_context = inp->sctp_context; n_inp->max_cwnd = inp->max_cwnd; diff --git a/usrsctplib/netinet/sctp_structs.h b/usrsctplib/netinet/sctp_structs.h index d569cc4a2..537a9e297 100755 --- a/usrsctplib/netinet/sctp_structs.h +++ b/usrsctplib/netinet/sctp_structs.h @@ -1259,6 +1259,7 @@ struct sctp_association { uint8_t reconfig_supported; uint8_t nrsack_supported; uint8_t pktdrop_supported; + uint8_t plpmtud_supported; uint8_t idata_supported; /* Did the peer make the stream config (add out) request */ diff --git a/usrsctplib/netinet/sctp_sysctl.c b/usrsctplib/netinet/sctp_sysctl.c index 83d2c9a2b..8dc9050ae 100755 --- a/usrsctplib/netinet/sctp_sysctl.c +++ b/usrsctplib/netinet/sctp_sysctl.c @@ -74,6 +74,7 @@ sctp_init_sysctls() SCTP_BASE_SYSCTL(sctp_reconfig_enable) = SCTPCTL_RECONFIG_ENABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_nrsack_enable) = SCTPCTL_NRSACK_ENABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_pktdrop_enable) = SCTPCTL_PKTDROP_ENABLE_DEFAULT; + SCTP_BASE_SYSCTL(sctp_plpmtud_enable) = SCTPCTL_PLPMTUD_ENABLE_DEFAULT; #if !(defined(__FreeBSD__) && __FreeBSD_version >= 800000) SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback) = SCTPCTL_LOOPBACK_NOCSUM_DEFAULT; #endif @@ -1252,6 +1253,7 @@ SYSCTL_PROC(_net_inet_sctp, OID_AUTO, asconf_enable, CTLFLAG_VNET|CTLTYPE_UINT|C SCTP_UINT_SYSCTL(reconfig_enable, sctp_reconfig_enable, SCTPCTL_RECONFIG_ENABLE) SCTP_UINT_SYSCTL(nrsack_enable, sctp_nrsack_enable, SCTPCTL_NRSACK_ENABLE) SCTP_UINT_SYSCTL(pktdrop_enable, sctp_pktdrop_enable, SCTPCTL_PKTDROP_ENABLE) +SCTP_UINT_SYSCTL(plpmtud_enable, sctp_plpmtud_enable, SCTPCTL_PLPMTUD_ENABLE) #if defined(__APPLE__) SCTP_UINT_SYSCTL(loopback_nocsum, sctp_no_csum_on_loopback, SCTPCTL_LOOPBACK_NOCSUM) #endif @@ -1360,6 +1362,7 @@ sctp_sysctl_handle_int(SYSCTL_HANDLER_ARGS) RANGECHK(SCTP_BASE_SYSCTL(sctp_reconfig_enable), SCTPCTL_RECONFIG_ENABLE_MIN, SCTPCTL_RECONFIG_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_nrsack_enable), SCTPCTL_NRSACK_ENABLE_MIN, SCTPCTL_NRSACK_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_pktdrop_enable), SCTPCTL_PKTDROP_ENABLE_MIN, SCTPCTL_PKTDROP_ENABLE_MAX); + RANGECHK(SCTP_BASE_SYSCTL(sctp_pktdrop_enable), SCTPCTL_PLPMTUD_ENABLE_MIN, SCTPCTL_PLPMTUD_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback), SCTPCTL_LOOPBACK_NOCSUM_MIN, SCTPCTL_LOOPBACK_NOCSUM_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_peer_chunk_oh), SCTPCTL_PEER_CHKOH_MIN, SCTPCTL_PEER_CHKOH_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_max_burst_default), SCTPCTL_MAXBURST_MIN, SCTPCTL_MAXBURST_MAX); @@ -1468,6 +1471,10 @@ sysctl_setup_sctp(void) &SCTP_BASE_SYSCTL(sctp_pktdrop_enable), 0, sctp_sysctl_handle_int, SCTPCTL_PKTDROP_ENABLE_DESC); + sysctl_add_oid(&sysctl_oid_top, "plpmtud_enable", CTLTYPE_INT|CTLFLAG_RW, + &SCTP_BASE_SYSCTL(sctp_plpmtud_enable), 0, sctp_sysctl_handle_int, + SCTPCTL_PLPMTUD_ENABLE_DESC); + sysctl_add_oid(&sysctl_oid_top, "loopback_nocsum", CTLTYPE_INT|CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback), 0, sctp_sysctl_handle_int, SCTPCTL_LOOPBACK_NOCSUM_DESC); diff --git a/usrsctplib/netinet/sctp_sysctl.h b/usrsctplib/netinet/sctp_sysctl.h index 4f10ff676..d779f2384 100755 --- a/usrsctplib/netinet/sctp_sysctl.h +++ b/usrsctplib/netinet/sctp_sysctl.h @@ -55,6 +55,7 @@ struct sctp_sysctl { uint32_t sctp_reconfig_enable; uint32_t sctp_nrsack_enable; uint32_t sctp_pktdrop_enable; + uint32_t sctp_plpmtud_enable; uint32_t sctp_fr_max_burst_default; #if !(defined(__FreeBSD__) && __FreeBSD_version >= 800000) uint32_t sctp_no_csum_on_loopback; @@ -216,6 +217,12 @@ struct sctp_sysctl { #define SCTPCTL_PKTDROP_ENABLE_MAX 1 #define SCTPCTL_PKTDROP_ENABLE_DEFAULT 0 +/* plpmtud_enable: Enable Packetization Layer Path MTU Discovery */ +#define SCTPCTL_PLPMTUD_ENABLE_DESC "Enable Packetization Layer PMTU Discovery" +#define SCTPCTL_PLPMTUD_ENABLE_MIN 0 +#define SCTPCTL_PLPMTUD_ENABLE_MAX 1 +#define SCTPCTL_PLPMTUD_ENABLE_DEFAULT 0 + /* loopback_nocsum: Enable NO Csum on packets sent on loopback */ #define SCTPCTL_LOOPBACK_NOCSUM_DESC "Enable NO Csum on packets sent on loopback" #define SCTPCTL_LOOPBACK_NOCSUM_MIN 0 diff --git a/usrsctplib/netinet/sctputil.c b/usrsctplib/netinet/sctputil.c index 2a07a5d5d..e9c7f83ab 100755 --- a/usrsctplib/netinet/sctputil.c +++ b/usrsctplib/netinet/sctputil.c @@ -1062,6 +1062,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, asoc->reconfig_supported = inp->reconfig_supported; asoc->nrsack_supported = inp->nrsack_supported; asoc->pktdrop_supported = inp->pktdrop_supported; + asoc->plpmtud_supported = inp->plpmtud_supported; asoc->idata_supported = inp->idata_supported; asoc->sctp_cmt_pf = (uint8_t)0; asoc->sctp_frag_point = inp->sctp_frag_point; diff --git a/usrsctplib/user_socket.c b/usrsctplib/user_socket.c index 31938f38d..d6b922aea 100755 --- a/usrsctplib/user_socket.c +++ b/usrsctplib/user_socket.c @@ -3537,6 +3537,7 @@ USRSCTP_SYSCTL_SET_DEF(sctp_asconf_enable, SCTPCTL_ASCONF_ENABLE) USRSCTP_SYSCTL_SET_DEF(sctp_reconfig_enable, SCTPCTL_RECONFIG_ENABLE) USRSCTP_SYSCTL_SET_DEF(sctp_nrsack_enable, SCTPCTL_NRSACK_ENABLE) USRSCTP_SYSCTL_SET_DEF(sctp_pktdrop_enable, SCTPCTL_PKTDROP_ENABLE) +USRSCTP_SYSCTL_SET_DEF(sctp_plpmtud_enable, SCTPCTL_PLPMTUD_ENABLE) USRSCTP_SYSCTL_SET_DEF(sctp_no_csum_on_loopback, SCTPCTL_LOOPBACK_NOCSUM) USRSCTP_SYSCTL_SET_DEF(sctp_peer_chunk_oh, SCTPCTL_PEER_CHKOH) USRSCTP_SYSCTL_SET_DEF(sctp_max_burst_default, SCTPCTL_MAXBURST) @@ -3616,6 +3617,7 @@ USRSCTP_SYSCTL_GET_DEF(sctp_asconf_enable) USRSCTP_SYSCTL_GET_DEF(sctp_reconfig_enable) USRSCTP_SYSCTL_GET_DEF(sctp_nrsack_enable) USRSCTP_SYSCTL_GET_DEF(sctp_pktdrop_enable) +USRSCTP_SYSCTL_GET_DEF(sctp_plpmtud_enable) USRSCTP_SYSCTL_GET_DEF(sctp_no_csum_on_loopback) USRSCTP_SYSCTL_GET_DEF(sctp_peer_chunk_oh) USRSCTP_SYSCTL_GET_DEF(sctp_max_burst_default) diff --git a/usrsctplib/usrsctp.h b/usrsctplib/usrsctp.h index e933c145e..de0cb9dc9 100644 --- a/usrsctplib/usrsctp.h +++ b/usrsctplib/usrsctp.h @@ -1049,6 +1049,7 @@ USRSCTP_SYSCTL_DECL(sctp_asconf_enable) USRSCTP_SYSCTL_DECL(sctp_reconfig_enable) USRSCTP_SYSCTL_DECL(sctp_nrsack_enable) USRSCTP_SYSCTL_DECL(sctp_pktdrop_enable) +USRSCTP_SYSCTL_DECL(sctp_plpmtud_enable) USRSCTP_SYSCTL_DECL(sctp_no_csum_on_loopback) USRSCTP_SYSCTL_DECL(sctp_peer_chunk_oh) USRSCTP_SYSCTL_DECL(sctp_max_burst_default) From 007f2850b56c4b025792e76cf28ff6798a12a34e Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Mon, 11 Jun 2018 10:33:33 +0200 Subject: [PATCH 10/11] More prerequisites without actually sending probes --- usrsctplib/netinet/sctp_input.c | 4 +++ usrsctplib/netinet/sctp_output.c | 46 +++++++++++++++++++++----------- usrsctplib/netinet/sctp_pcb.c | 12 +++++++++ 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c index f2afd5425..227690035 100755 --- a/usrsctplib/netinet/sctp_input.c +++ b/usrsctplib/netinet/sctp_input.c @@ -935,6 +935,10 @@ sctp_start_net_timers(struct sctp_tcb *stcb) sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) && (cnt_hb_sent < SCTP_BASE_SYSCTL(sctp_hb_maxburst))) { + if (stcb->asoc.plpmtud_supported) { + net->probing_state = SCTP_PROBE_NONE; + net->probe_mtu = 0; + } sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); cnt_hb_sent++; } diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 6e4a5ca51..0bfc79bc0 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -4487,9 +4487,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { sctp_mtu_size_reset(inp, &stcb->asoc, mtu); } - net->mtu = mtu; net->got_max = 1; - sctp_pathmtu_adjustment(stcb, net->mtu, net); + if (!stcb->sctp_ep->plpmtud_supported) { + net->mtu = mtu; + sctp_pathmtu_adjustment(stcb, net->mtu, net); + } } #else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); @@ -4958,27 +4960,39 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, #if defined(__Userspace__) mtu = sctp_get_mtu_from_addr(inp, (struct sockaddr *)&(net->ro._s_addr->address.sin)); - if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { - if (net->port) { - mtu -= sizeof(struct udphdr); + if (!stcb->sctp_ep->plpmtud_supported) { + if (mtu > 0 && (mtu < net->mtu || !net->got_max)) { + if (net->port) { + mtu -= sizeof(struct udphdr); + } + if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + } + net->mtu = mtu; + net->got_max = 1; + sctp_pathmtu_adjustment(stcb, net->mtu, net); } - if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { - sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + } else { + if (net->port) { + net->mtu -= sizeof(struct udphdr); } - net->mtu = mtu; - net->got_max = 1; - sctp_pathmtu_adjustment(stcb, net->mtu, net); } #else mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); if (mtu > 0) { - if (net->port) { - mtu -= sizeof(struct udphdr); - } - if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { - sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + if (!stcb->sctp_ep->plpmtud_supported) { + if (net->port) { + mtu -= sizeof(struct udphdr); + } + if ((stcb != NULL) && (stcb->asoc.smallest_mtu > mtu)) { + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + } + net->mtu = mtu; + } else { + if (net->port) { + net->mtu -= sizeof(struct udphdr); + } } - net->mtu = mtu; net->got_max = 1; } #endif diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index 51c50f7b8..ac09ffd7b 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -4618,6 +4618,12 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, net->RTO_measured = 0; stcb->asoc.numnets++; net->ref_count = 1; + if (stcb->asoc.plpmtud_supported) { + net->probe_mtu = 0; + net->mtu_probing = 0; + net->probing_state = SCTP_PROBE_NONE; + net->got_max = 0; + } net->cwr_window_tsn = net->last_cwr_tsn = stcb->asoc.sending_seq - 1; net->port = port; net->dscp = stcb->asoc.default_dscp; @@ -4727,6 +4733,12 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, /* Start things off to match mtu of interface please. */ SCTP_SET_MTU_OF_ROUTE(&net->ro._l_addr.sa, net->ro.ro_rt, net->mtu); + } else if (stcb->asoc.plpmtud_supported) { + net->mtu = min(rmtu, SCTP_DEFAULT_MTU); + } + if (stcb->asoc.plpmtud_supported) { + net->max_mtu = max(net->mtu, rmtu); + net->max_mtu -= net->max_mtu % 4; } net->got_max = 1; } From 1e39e726925f9f1f5ae74d3a9618665476972d79 Mon Sep 17 00:00:00 2001 From: Irene Ruengeler Date: Tue, 12 Jun 2018 06:34:52 +0200 Subject: [PATCH 11/11] Send and receive probe packets --- usrsctplib/netinet/sctp_input.c | 122 +++++++++++++++++++++++++++++++ usrsctplib/netinet/sctp_output.c | 46 ++++++++---- usrsctplib/netinet/sctp_output.h | 2 +- usrsctplib/netinet/sctp_pcb.c | 22 ++++++ usrsctplib/netinet/sctputil.c | 22 +----- 5 files changed, 179 insertions(+), 35 deletions(-) diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c index 227690035..0861d7c0a 100755 --- a/usrsctplib/netinet/sctp_input.c +++ b/usrsctplib/netinet/sctp_input.c @@ -775,6 +775,112 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, stcb->asoc.deleted_primary); } } + if (stcb->sctp_ep->plpmtud_supported) { + uint32_t next_mtu; + if (r_net->probing_state < SCTP_PROBE_BASE && !r_net->mtu_probing) + r_net->mtu_probing = 1; + if (r_net->mtu_probing) { + if (r_net->probe_counts > 0) + r_net->probe_counts = 0; + switch (r_net->probing_state) { + case SCTP_PROBE_NONE: + r_net->probing_state = SCTP_PROBE_BASE; + r_net->probed_mtu = SCTP_PROBE_MIN; +#ifdef INET6 + if (cp->heartbeat.hb_info.addr_family == AF_INET6) { + r_net->probe_mtu = SCTP_PROBE_MTU_V6_BASE; + } +#endif +#ifdef INET + if (cp->heartbeat.hb_info.addr_family == AF_INET) { + r_net->probe_mtu = SCTP_PROBE_MTU_V4_BASE; + } +#endif + sctp_send_a_probe(stcb->sctp_ep, stcb, r_net); + break; + case SCTP_PROBE_ERROR: + r_net->probed_mtu = SCTP_PROBE_MIN; + r_net->probe_mtu = sctp_get_next_mtu(SCTP_PROBE_MIN); + r_net->probing_state = SCTP_PROBE_SEARCH_UP; + sctp_send_a_probe(stcb->sctp_ep, stcb, r_net); + break; + case SCTP_PROBE_BASE: + r_net->probed_mtu = cp->heartbeat.hb_info.probe_mtu; + if (r_net->probed_mtu == r_net->max_mtu) { + sctp_pathmtu_adjustment(stcb, r_net->probed_mtu, r_net); + r_net->mtu_probing = 0; + r_net->probing_state = SCTP_PROBE_DONE; + r_net->mtu = r_net->max_mtu; + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_7); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); + if (SCTP_OS_TIMER_PENDING(&r_net->pmtu_timer.timer)) { + sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net, + SCTP_FROM_SCTP_INPUT + SCTP_LOC_8); + } + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net); + break; + } + if (SCTP_PROBE_UP == 1) { + r_net->probing_state = SCTP_PROBE_SEARCH_UP; + if (r_net->max_mtu == 0) { + r_net->probe_mtu = sctp_get_next_mtu(r_net->probed_mtu); + } else { + r_net->probe_mtu = min(r_net->max_mtu, sctp_get_next_mtu(r_net->probed_mtu)); + } + } else { + r_net->probing_state = SCTP_PROBE_SEARCH_DOWN; + r_net->probe_mtu = r_net->max_mtu; + } + r_net->mtu = r_net->probed_mtu; + + sctp_pathmtu_adjustment(stcb, r_net->probed_mtu, r_net); + sctp_send_a_probe(stcb->sctp_ep, stcb, r_net); + break; + case SCTP_PROBE_SEARCH_UP: + r_net->probed_mtu = cp->heartbeat.hb_info.probe_mtu; + if (r_net->probed_mtu == r_net->max_mtu) { + r_net->mtu = r_net->max_mtu; + sctp_pathmtu_adjustment(stcb, r_net->max_mtu, r_net); + r_net->mtu_probing = 0; + r_net->probing_state = SCTP_PROBE_DONE; + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_9); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); + if (SCTP_OS_TIMER_PENDING(&r_net->pmtu_timer.timer)) { + sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net, + SCTP_FROM_SCTP_INPUT + SCTP_LOC_10); + } + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net); + break; + } + next_mtu = sctp_get_next_mtu(r_net->probed_mtu); + if (next_mtu > r_net->probe_mtu) + r_net->probe_mtu = next_mtu; + if (r_net->max_mtu > 0 && next_mtu > r_net->max_mtu) { + r_net->probe_mtu = r_net->max_mtu; + } + r_net->mtu = r_net->probed_mtu; + sctp_pathmtu_adjustment(stcb, r_net->probed_mtu, r_net); + sctp_send_a_probe(stcb->sctp_ep, stcb, r_net); + break; + case SCTP_PROBE_SEARCH_DOWN: /* Highest MTU reached. Stop Probing */ + r_net->probed_mtu = cp->heartbeat.hb_info.probe_mtu; + r_net->mtu = r_net->probed_mtu; + sctp_pathmtu_adjustment(stcb, r_net->mtu, r_net); + r_net->mtu_probing = 0; + r_net->probing_state = SCTP_PROBE_DONE; + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_11); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); + if (SCTP_OS_TIMER_PENDING(&r_net->pmtu_timer.timer)) { + sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net, + SCTP_FROM_SCTP_INPUT + SCTP_LOC_12); + } + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, r_net); + break; + case SCTP_PROBE_DONE: + break; + } + } + } } static int @@ -941,6 +1047,22 @@ sctp_start_net_timers(struct sctp_tcb *stcb) } sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); cnt_hb_sent++; + } else if (stcb->asoc.plpmtud_supported) { + net->probing_state = SCTP_PROBE_BASE; +#ifdef INET6 + if (stcb->asoc.scope.ipv6_addr_legal) { + net->probe_mtu = SCTP_PROBE_MTU_V6_BASE; + } +#endif +#ifdef INET + if (stcb->asoc.scope.ipv4_addr_legal) { + net->probe_mtu = SCTP_PROBE_MTU_V4_BASE; + } +#endif + net->probed_mtu = SCTP_PROBE_MIN; + net->mtu_probing = 1; + net->probe_counts = 0; + sctp_send_a_probe(stcb->sctp_ep, stcb, net); } } if (cnt_hb_sent) { diff --git a/usrsctplib/netinet/sctp_output.c b/usrsctplib/netinet/sctp_output.c index 0bfc79bc0..8e23b1d11 100755 --- a/usrsctplib/netinet/sctp_output.c +++ b/usrsctplib/netinet/sctp_output.c @@ -11631,13 +11631,13 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, uint32_t vtag, uint8_t type, struct mbuf *cause, uint8_t mflowtype, uint32_t mflowid, uint16_t fibnum, - uint32_t vrf_id, uint16_t port) + uint32_t vrf_id, uint16_t port, struct sctp_tcb *stcb) #else void sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, uint32_t vtag, uint8_t type, struct mbuf *cause, - uint32_t vrf_id SCTP_UNUSED, uint16_t port) + uint32_t vrf_id SCTP_UNUSED, uint16_t port, struct sctp_tcb *stcb) #endif { #ifdef __Panda__ @@ -11688,7 +11688,11 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, padding_len = 0; } /* Get an mbuf for the header. */ - len = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr); + if (type == SCTP_PAD_CHUNK) { + len = sizeof(struct sctphdr); + } else { + len = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr); + } switch (dst->sa_family) { #ifdef INET case AF_INET: @@ -11852,15 +11856,17 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, shout->v_tag = sh->v_tag; } len += sizeof(struct sctphdr); - ch = (struct sctp_chunkhdr *)((caddr_t)shout + sizeof(struct sctphdr)); - ch->chunk_type = type; - if (vtag) { - ch->chunk_flags = 0; - } else { - ch->chunk_flags = SCTP_HAD_NO_TCB; + if (type != SCTP_PAD_CHUNK) { + ch = (struct sctp_chunkhdr *)((caddr_t)shout + sizeof(struct sctphdr)); + ch->chunk_type = type; + if (vtag) { + ch->chunk_flags = 0; + } else { + ch->chunk_flags = SCTP_HAD_NO_TCB; + } + ch->chunk_length = htons((uint16_t)(sizeof(struct sctp_chunkhdr) + cause_len)); + len += sizeof(struct sctp_chunkhdr); } - ch->chunk_length = htons((uint16_t)(sizeof(struct sctp_chunkhdr) + cause_len)); - len += sizeof(struct sctp_chunkhdr); len += cause_len + padding_len; if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { @@ -11939,7 +11945,11 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, ro.ro_rt = NULL; } #else - SCTP_IP_OUTPUT(ret, o_pak, NULL, NULL, vrf_id); + if (type == SCTP_PAD_CHUNK) { + SCTP_IP_OUTPUT(ret, o_pak, NULL, stcb, vrf_id); + } else { + SCTP_IP_OUTPUT(ret, o_pak, NULL, NULL, vrf_id); + } #endif break; #endif @@ -12037,7 +12047,7 @@ sctp_send_shutdown_complete2(struct sockaddr *src, struct sockaddr *dst, #if defined(__FreeBSD__) mflowtype, mflowid, fibnum, #endif - vrf_id, port); + vrf_id, port, NULL); } void @@ -12174,7 +12184,11 @@ sctp_send_hb(struct sctp_tcb *stcb, struct sctp_nets *net,int so_locked } sctp_free_a_chunk(stcb, chk, so_locked); return; - break; + } + if (stcb->sctp_ep->plpmtud_supported && net->mtu_probing) { + hb->heartbeat.hb_info.probe_mtu = net->probe_mtu; + } else { + hb->heartbeat.hb_info.probe_mtu = 0; } net->hb_responded = 0; TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next); @@ -13069,7 +13083,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sockaddr *src, struct sockadd #if defined(__FreeBSD__) mflowtype, mflowid, fibnum, #endif - vrf_id, port); + vrf_id, port, NULL); return; } @@ -13085,7 +13099,7 @@ sctp_send_operr_to(struct sockaddr *src, struct sockaddr *dst, #if defined(__FreeBSD__) mflowtype, mflowid, fibnum, #endif - vrf_id, port); + vrf_id, port, NULL); return; } diff --git a/usrsctplib/netinet/sctp_output.h b/usrsctplib/netinet/sctp_output.h index c5c607851..ee6fa71a9 100755 --- a/usrsctplib/netinet/sctp_output.h +++ b/usrsctplib/netinet/sctp_output.h @@ -244,7 +244,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst, #if defined(__FreeBSD__) uint8_t mflowtype, uint32_t mflowid, uint16_t fibnum, #endif - uint32_t vrf_id, uint16_t port); + uint32_t vrf_id, uint16_t port, struct sctp_tcb *stcb); void sctp_send_operr_to(struct sockaddr *, struct sockaddr *, struct sctphdr *, uint32_t, struct mbuf *, diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c index ac09ffd7b..392372924 100755 --- a/usrsctplib/netinet/sctp_pcb.c +++ b/usrsctplib/netinet/sctp_pcb.c @@ -4922,6 +4922,28 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, TAILQ_INSERT_HEAD(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); } + if (stcb->asoc.plpmtud_supported) { + net->probing_state = SCTP_PROBE_NONE; + net->probe_mtu = 0; + if (from == SCTP_ADDR_DYNAMIC_ADDED) { + net->probing_state = SCTP_PROBE_BASE; +#ifdef INET + if (newaddr->sa_family == AF_INET) { + net->probe_mtu = SCTP_PROBE_MTU_V4_BASE; + } +#endif +#ifdef INET6 + if (newaddr->sa_family == AF_INET6) { + net->probe_mtu = SCTP_PROBE_MTU_V6_BASE; + } +#endif + net->probed_mtu = SCTP_PROBE_MIN; + net->mtu_probing = 1; + net->probe_counts = 0; + net->mtu = net->probe_mtu; + sctp_send_a_probe(stcb->sctp_ep, stcb, net); + } + } return (0); } diff --git a/usrsctplib/netinet/sctputil.c b/usrsctplib/netinet/sctputil.c index e9c7f83ab..f92da1395 100755 --- a/usrsctplib/netinet/sctputil.c +++ b/usrsctplib/netinet/sctputil.c @@ -2175,7 +2175,8 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, to_ticks = to_ticks - jitter; } if (!(net->dest_state & SCTP_ADDR_UNCONFIRMED) && - !(net->dest_state & SCTP_ADDR_PF)) { + !(net->dest_state & SCTP_ADDR_PF) && + !(net->mtu_probing)) { to_ticks += net->heart_beat_delay; } /* @@ -8288,22 +8289,7 @@ sctp_make_hb(struct sctp_tcb *stcb, struct sctp_nets *net,int so_locked return NULL; } (void)SCTP_GETTIME_TIMEVAL(&now); - switch (net->ro._l_addr.sa.sa_family) { -#ifdef INET - case AF_INET: - break; -#endif -#ifdef INET6 - case AF_INET6: - break; -#endif -#if defined(__Userspace__) - case AF_CONN: - break; -#endif - default: - return NULL; - } + send_size = sizeof(struct sctp_heartbeat_chunk); chk = sctp_get_mbuf_for_msg(send_size, 0, M_NOWAIT, 1, MT_HEADER); @@ -8549,7 +8535,7 @@ sctp_send_a_probe(struct sctp_inpcb *inp, #if defined(__FreeBSD__) net->flowtype, net->flowid, inp->fibnum, #endif - vrf_id, net->port); + vrf_id, net->port, stcb); sctp_m_freem(m); if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net,