Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_syncache.c
Show First 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | |||||
#include <netinet/tcp_var.h> | #include <netinet/tcp_var.h> | ||||
#include <netinet/tcp_syncache.h> | #include <netinet/tcp_syncache.h> | ||||
#ifdef INET6 | #ifdef INET6 | ||||
#include <netinet6/tcp6_var.h> | #include <netinet6/tcp6_var.h> | ||||
#endif | #endif | ||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
#include <netinet/toecore.h> | #include <netinet/toecore.h> | ||||
#endif | #endif | ||||
#include <netinet/udp.h> | |||||
#include <netinet/udp_var.h> | |||||
#include <netipsec/ipsec_support.h> | #include <netipsec/ipsec_support.h> | ||||
#include <machine/in_cksum.h> | #include <machine/in_cksum.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
VNET_DEFINE_STATIC(int, tcp_syncookies) = 1; | VNET_DEFINE_STATIC(int, tcp_syncookies) = 1; | ||||
Show All 31 Lines | |||||
static void syncache_timer(void *); | static void syncache_timer(void *); | ||||
static uint32_t syncookie_mac(struct in_conninfo *, tcp_seq, uint8_t, | static uint32_t syncookie_mac(struct in_conninfo *, tcp_seq, uint8_t, | ||||
uint8_t *, uintptr_t); | uint8_t *, uintptr_t); | ||||
static tcp_seq syncookie_generate(struct syncache_head *, struct syncache *); | static tcp_seq syncookie_generate(struct syncache_head *, struct syncache *); | ||||
static struct syncache | static struct syncache | ||||
*syncookie_lookup(struct in_conninfo *, struct syncache_head *, | *syncookie_lookup(struct in_conninfo *, struct syncache_head *, | ||||
struct syncache *, struct tcphdr *, struct tcpopt *, | struct syncache *, struct tcphdr *, struct tcpopt *, | ||||
struct socket *); | struct socket *, uint16_t); | ||||
static void syncache_pause(struct in_conninfo *); | static void syncache_pause(struct in_conninfo *); | ||||
static void syncache_unpause(void *); | static void syncache_unpause(void *); | ||||
static void syncookie_reseed(void *); | static void syncookie_reseed(void *); | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
static int syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, | static int syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, | ||||
struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | ||||
struct socket *lso); | struct socket *lso, uint16_t port); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Transmit the SYN,ACK fewer times than TCP_MAXRXTSHIFT specifies. | * Transmit the SYN,ACK fewer times than TCP_MAXRXTSHIFT specifies. | ||||
* 3 retransmits corresponds to a timeout with default values of | * 3 retransmits corresponds to a timeout with default values of | ||||
* tcp_rexmit_initial * ( 1 + | * tcp_rexmit_initial * ( 1 + | ||||
* tcp_backoff[1] + | * tcp_backoff[1] + | ||||
* tcp_backoff[2] + | * tcp_backoff[2] + | ||||
▲ Show 20 Lines • Show All 438 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* This function is called when we get a RST for a | * This function is called when we get a RST for a | ||||
* non-existent connection, so that we can see if the | * non-existent connection, so that we can see if the | ||||
* connection is in the syn cache. If it is, zap it. | * connection is in the syn cache. If it is, zap it. | ||||
* If required send a challenge ACK. | * If required send a challenge ACK. | ||||
*/ | */ | ||||
void | void | ||||
syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m) | syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m, | ||||
uint16_t port) | |||||
{ | { | ||||
struct syncache *sc; | struct syncache *sc; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
char *s = NULL; | char *s = NULL; | ||||
if (syncache_cookiesonly()) | if (syncache_cookiesonly()) | ||||
return; | return; | ||||
sc = syncache_lookup(inc, &sch); /* returns locked sch */ | sc = syncache_lookup(inc, &sch); /* returns locked sch */ | ||||
Show All 23 Lines | if (sc == NULL) { | ||||
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | ||||
log(LOG_DEBUG, "%s; %s: Spurious RST without matching " | log(LOG_DEBUG, "%s; %s: Spurious RST without matching " | ||||
"syncache entry (possibly syncookie only), " | "syncache entry (possibly syncookie only), " | ||||
"segment ignored\n", s, __func__); | "segment ignored\n", s, __func__); | ||||
TCPSTAT_INC(tcps_badrst); | TCPSTAT_INC(tcps_badrst); | ||||
goto done; | goto done; | ||||
} | } | ||||
/* The remote UDP encaps port does not match. */ | |||||
if (sc->sc_port != port) { | |||||
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | |||||
log(LOG_DEBUG, "%s; %s: Spurious RST with matching " | |||||
"syncache entry but non-matching UDP encaps port, " | |||||
"segment ignored\n", s, __func__); | |||||
TCPSTAT_INC(tcps_badrst); | |||||
goto done; | |||||
} | |||||
/* | /* | ||||
* If the RST bit is set, check the sequence number to see | * If the RST bit is set, check the sequence number to see | ||||
* if this is a valid reset segment. | * if this is a valid reset segment. | ||||
* | * | ||||
* RFC 793 page 37: | * RFC 793 page 37: | ||||
* In all states except SYN-SENT, all reset (RST) segments | * In all states except SYN-SENT, all reset (RST) segments | ||||
* are validated by checking their SEQ-fields. A reset is | * are validated by checking their SEQ-fields. A reset is | ||||
* valid if its sequence number is in the window. | * valid if its sequence number is in the window. | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
done: | done: | ||||
if (s != NULL) | if (s != NULL) | ||||
free(s, M_TCPLOG); | free(s, M_TCPLOG); | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
} | } | ||||
void | void | ||||
syncache_badack(struct in_conninfo *inc) | syncache_badack(struct in_conninfo *inc, uint16_t port) | ||||
{ | { | ||||
struct syncache *sc; | struct syncache *sc; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
if (syncache_cookiesonly()) | if (syncache_cookiesonly()) | ||||
return; | return; | ||||
sc = syncache_lookup(inc, &sch); /* returns locked sch */ | sc = syncache_lookup(inc, &sch); /* returns locked sch */ | ||||
SCH_LOCK_ASSERT(sch); | SCH_LOCK_ASSERT(sch); | ||||
if (sc != NULL) { | if ((sc != NULL) && (sc->sc_port == port)) { | ||||
syncache_drop(sc, sch); | syncache_drop(sc, sch); | ||||
TCPSTAT_INC(tcps_sc_badack); | TCPSTAT_INC(tcps_sc_badack); | ||||
} | } | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
} | } | ||||
void | void | ||||
syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq) | syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq, uint16_t port) | ||||
{ | { | ||||
struct syncache *sc; | struct syncache *sc; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
if (syncache_cookiesonly()) | if (syncache_cookiesonly()) | ||||
return; | return; | ||||
sc = syncache_lookup(inc, &sch); /* returns locked sch */ | sc = syncache_lookup(inc, &sch); /* returns locked sch */ | ||||
SCH_LOCK_ASSERT(sch); | SCH_LOCK_ASSERT(sch); | ||||
if (sc == NULL) | if (sc == NULL) | ||||
goto done; | goto done; | ||||
/* If the port != sc_port, then it's a bogus ICMP msg */ | |||||
if (port != sc->sc_port) | |||||
goto done; | |||||
/* If the sequence number != sc_iss, then it's a bogus ICMP msg */ | /* If the sequence number != sc_iss, then it's a bogus ICMP msg */ | ||||
if (ntohl(th_seq) != sc->sc_iss) | if (ntohl(th_seq) != sc->sc_iss) | ||||
goto done; | goto done; | ||||
/* | /* | ||||
* If we've rertransmitted 3 times and this is our second error, | * If we've rertransmitted 3 times and this is our second error, | ||||
* we remove the entry. Otherwise, we allow it to continue on. | * we remove the entry. Otherwise, we allow it to continue on. | ||||
* This prevents us from incorrectly nuking an entry during a | * This prevents us from incorrectly nuking an entry during a | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | #if defined(IPSEC) || defined(IPSEC_SUPPORT) | ||||
if (ipsec_copy_pcbpolicy(sotoinpcb(lso), inp) != 0) | if (ipsec_copy_pcbpolicy(sotoinpcb(lso), inp) != 0) | ||||
printf("syncache_socket: could not copy policy\n"); | printf("syncache_socket: could not copy policy\n"); | ||||
#endif | #endif | ||||
INP_HASH_WUNLOCK(&V_tcbinfo); | INP_HASH_WUNLOCK(&V_tcbinfo); | ||||
tp = intotcpcb(inp); | tp = intotcpcb(inp); | ||||
tcp_state_change(tp, TCPS_SYN_RECEIVED); | tcp_state_change(tp, TCPS_SYN_RECEIVED); | ||||
tp->iss = sc->sc_iss; | tp->iss = sc->sc_iss; | ||||
tp->irs = sc->sc_irs; | tp->irs = sc->sc_irs; | ||||
tp->t_port = sc->sc_port; | |||||
tcp_rcvseqinit(tp); | tcp_rcvseqinit(tp); | ||||
tcp_sendseqinit(tp); | tcp_sendseqinit(tp); | ||||
blk = sototcpcb(lso)->t_fb; | blk = sototcpcb(lso)->t_fb; | ||||
if (V_functions_inherit_listen_socket_stack && blk != tp->t_fb) { | if (V_functions_inherit_listen_socket_stack && blk != tp->t_fb) { | ||||
/* | /* | ||||
* Our parents t_fb was not the default, | * Our parents t_fb was not the default, | ||||
* we need to release our ref on tp->t_fb and | * we need to release our ref on tp->t_fb and | ||||
* pickup one on the new entry. | * pickup one on the new entry. | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | |||||
* the cache and turn it into a full-blown connection in | * the cache and turn it into a full-blown connection in | ||||
* the SYN-RECEIVED state. | * the SYN-RECEIVED state. | ||||
* | * | ||||
* On syncache_socket() success the newly created socket | * On syncache_socket() success the newly created socket | ||||
* has its underlying inp locked. | * has its underlying inp locked. | ||||
*/ | */ | ||||
int | int | ||||
syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | ||||
struct socket **lsop, struct mbuf *m) | struct socket **lsop, struct mbuf *m, uint16_t port) | ||||
{ | { | ||||
struct syncache *sc; | struct syncache *sc; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
struct syncache scs; | struct syncache scs; | ||||
char *s; | char *s; | ||||
bool locked; | bool locked; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
Show All 11 Lines | syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | ||||
} | } | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
/* | /* | ||||
* Test code for syncookies comparing the syncache stored | * Test code for syncookies comparing the syncache stored | ||||
* values with the reconstructed values from the cookie. | * values with the reconstructed values from the cookie. | ||||
*/ | */ | ||||
if (sc != NULL) | if (sc != NULL) | ||||
syncookie_cmp(inc, sch, sc, th, to, *lsop); | syncookie_cmp(inc, sch, sc, th, to, *lsop, port); | ||||
#endif | #endif | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
/* | /* | ||||
* There is no syncache entry, so see if this ACK is | * There is no syncache entry, so see if this ACK is | ||||
* a returning syncookie. To do this, first: | * a returning syncookie. To do this, first: | ||||
* A. Check if syncookies are used in case of syncache | * A. Check if syncookies are used in case of syncache | ||||
* overflows | * overflows | ||||
Show All 17 Lines | if (locked && !V_tcp_syncookiesonly && | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | ||||
log(LOG_DEBUG, "%s; %s: Spurious ACK, " | log(LOG_DEBUG, "%s; %s: Spurious ACK, " | ||||
"segment rejected (no syncache entry)\n", | "segment rejected (no syncache entry)\n", | ||||
s, __func__); | s, __func__); | ||||
goto failed; | goto failed; | ||||
} | } | ||||
bzero(&scs, sizeof(scs)); | bzero(&scs, sizeof(scs)); | ||||
sc = syncookie_lookup(inc, sch, &scs, th, to, *lsop); | sc = syncookie_lookup(inc, sch, &scs, th, to, *lsop, port); | ||||
if (locked) | if (locked) | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | if ((s = tcp_log_addrs(inc, th, NULL, NULL))) | ||||
log(LOG_DEBUG, "%s; %s: Segment failed " | log(LOG_DEBUG, "%s; %s: Segment failed " | ||||
"SYNCOOKIE authentication, segment rejected " | "SYNCOOKIE authentication, segment rejected " | ||||
"(probably spoofed)\n", s, __func__); | "(probably spoofed)\n", s, __func__); | ||||
goto failed; | goto failed; | ||||
Show All 10 Lines | if ((to->to_flags & TOF_SIGNATURE) != 0 && | ||||
s, __func__); | s, __func__); | ||||
free(s, M_TCPLOG); | free(s, M_TCPLOG); | ||||
} | } | ||||
TCPSTAT_INC(tcps_sig_err_sigopt); | TCPSTAT_INC(tcps_sig_err_sigopt); | ||||
return (-1); /* Do not send RST */ | return (-1); /* Do not send RST */ | ||||
} | } | ||||
#endif /* TCP_SIGNATURE */ | #endif /* TCP_SIGNATURE */ | ||||
} else { | } else { | ||||
if (sc->sc_port != port) { | |||||
SCH_UNLOCK(sch); | |||||
return (0); | |||||
} | |||||
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE) | #if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE) | ||||
/* | /* | ||||
* If listening socket requested TCP digests, check that | * If listening socket requested TCP digests, check that | ||||
* received ACK has signature and it is correct. | * received ACK has signature and it is correct. | ||||
* If not, drop the ACK and leave sc entry in th cache, | * If not, drop the ACK and leave sc entry in th cache, | ||||
* because SYN was received with correct signature. | * because SYN was received with correct signature. | ||||
*/ | */ | ||||
if (sc->sc_flags & SCF_SIGNATURE) { | if (sc->sc_flags & SCF_SIGNATURE) { | ||||
▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Lines | |||||
* cookie is processed and a new socket is created. In this case, any data | * cookie is processed and a new socket is created. In this case, any data | ||||
* accompanying the SYN will be queued to the socket by tcp_input() and will | * accompanying the SYN will be queued to the socket by tcp_input() and will | ||||
* be ACKed either when the application sends response data or the delayed | * be ACKed either when the application sends response data or the delayed | ||||
* ACK timer expires, whichever comes first. | * ACK timer expires, whichever comes first. | ||||
*/ | */ | ||||
int | int | ||||
syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | ||||
struct inpcb *inp, struct socket **lsop, struct mbuf *m, void *tod, | struct inpcb *inp, struct socket **lsop, struct mbuf *m, void *tod, | ||||
void *todctx, uint8_t iptos) | void *todctx, uint8_t iptos, uint16_t port) | ||||
{ | { | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct socket *so; | struct socket *so; | ||||
struct syncache *sc = NULL; | struct syncache *sc = NULL; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
struct mbuf *ipopts = NULL; | struct mbuf *ipopts = NULL; | ||||
u_int ltflags; | u_int ltflags; | ||||
int win, ip_ttl, ip_tos; | int win, ip_ttl, ip_tos; | ||||
▲ Show 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | skip_alloc: | ||||
/* | /* | ||||
* Fill in the syncache values. | * Fill in the syncache values. | ||||
*/ | */ | ||||
#ifdef MAC | #ifdef MAC | ||||
sc->sc_label = maclabel; | sc->sc_label = maclabel; | ||||
#endif | #endif | ||||
sc->sc_cred = cred; | sc->sc_cred = cred; | ||||
sc->sc_port = port; | |||||
cred = NULL; | cred = NULL; | ||||
sc->sc_ipopts = ipopts; | sc->sc_ipopts = ipopts; | ||||
bcopy(inc, &sc->sc_inc, sizeof(struct in_conninfo)); | bcopy(inc, &sc->sc_inc, sizeof(struct in_conninfo)); | ||||
sc->sc_ip_tos = ip_tos; | sc->sc_ip_tos = ip_tos; | ||||
sc->sc_ip_ttl = ip_ttl; | sc->sc_ip_ttl = ip_ttl; | ||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
sc->sc_tod = tod; | sc->sc_tod = tod; | ||||
sc->sc_todctx = todctx; | sc->sc_todctx = todctx; | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | |||||
* i.e. m0 != NULL, or upon 3WHS ACK timeout, i.e. m0 == NULL. | * i.e. m0 != NULL, or upon 3WHS ACK timeout, i.e. m0 == NULL. | ||||
*/ | */ | ||||
static int | static int | ||||
syncache_respond(struct syncache *sc, const struct mbuf *m0, int flags) | syncache_respond(struct syncache *sc, const struct mbuf *m0, int flags) | ||||
{ | { | ||||
struct ip *ip = NULL; | struct ip *ip = NULL; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct tcphdr *th = NULL; | struct tcphdr *th = NULL; | ||||
struct udphdr *udp = NULL; | |||||
int optlen, error = 0; /* Make compiler happy */ | int optlen, error = 0; /* Make compiler happy */ | ||||
u_int16_t hlen, tlen, mssopt; | u_int16_t hlen, tlen, mssopt, ulen; | ||||
struct tcpopt to; | struct tcpopt to; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct ip6_hdr *ip6 = NULL; | struct ip6_hdr *ip6 = NULL; | ||||
#endif | #endif | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
hlen = | hlen = | ||||
#ifdef INET6 | #ifdef INET6 | ||||
(sc->sc_inc.inc_flags & INC_ISIPV6) ? sizeof(struct ip6_hdr) : | (sc->sc_inc.inc_flags & INC_ISIPV6) ? sizeof(struct ip6_hdr) : | ||||
#endif | #endif | ||||
sizeof(struct ip); | sizeof(struct ip); | ||||
tlen = hlen + sizeof(struct tcphdr); | tlen = hlen + sizeof(struct tcphdr); | ||||
if (sc->sc_port) { | |||||
tlen += sizeof(struct udphdr); | |||||
} | |||||
/* Determine MSS we advertize to other end of connection. */ | /* Determine MSS we advertize to other end of connection. */ | ||||
mssopt = max(tcp_mssopt(&sc->sc_inc), V_tcp_minmss); | mssopt = tcp_mssopt(&sc->sc_inc); | ||||
if (sc->sc_port) | |||||
mssopt -= V_tcp_udp_tunneling_overhead; | |||||
mssopt = max(mssopt, V_tcp_minmss); | |||||
/* XXX: Assume that the entire packet will fit in a header mbuf. */ | /* XXX: Assume that the entire packet will fit in a header mbuf. */ | ||||
KASSERT(max_linkhdr + tlen + TCP_MAXOLEN <= MHLEN, | KASSERT(max_linkhdr + tlen + TCP_MAXOLEN <= MHLEN, | ||||
("syncache: mbuf too small")); | ("syncache: mbuf too small")); | ||||
/* Create the IP+TCP header from scratch. */ | /* Create the IP+TCP header from scratch. */ | ||||
m = m_gethdr(M_NOWAIT, MT_DATA); | m = m_gethdr(M_NOWAIT, MT_DATA); | ||||
if (m == NULL) | if (m == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_syncache_create_mbuf(sc->sc_label, m); | mac_syncache_create_mbuf(sc->sc_label, m); | ||||
#endif | #endif | ||||
m->m_data += max_linkhdr; | m->m_data += max_linkhdr; | ||||
m->m_len = tlen; | m->m_len = tlen; | ||||
m->m_pkthdr.len = tlen; | m->m_pkthdr.len = tlen; | ||||
m->m_pkthdr.rcvif = NULL; | m->m_pkthdr.rcvif = NULL; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if (sc->sc_inc.inc_flags & INC_ISIPV6) { | if (sc->sc_inc.inc_flags & INC_ISIPV6) { | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
ip6->ip6_vfc = IPV6_VERSION; | ip6->ip6_vfc = IPV6_VERSION; | ||||
ip6->ip6_nxt = IPPROTO_TCP; | |||||
ip6->ip6_src = sc->sc_inc.inc6_laddr; | ip6->ip6_src = sc->sc_inc.inc6_laddr; | ||||
ip6->ip6_dst = sc->sc_inc.inc6_faddr; | ip6->ip6_dst = sc->sc_inc.inc6_faddr; | ||||
ip6->ip6_plen = htons(tlen - hlen); | ip6->ip6_plen = htons(tlen - hlen); | ||||
/* ip6_hlim is set after checksum */ | /* ip6_hlim is set after checksum */ | ||||
/* Zero out traffic class and flow label. */ | /* Zero out traffic class and flow label. */ | ||||
ip6->ip6_flow &= ~IPV6_FLOWINFO_MASK; | ip6->ip6_flow &= ~IPV6_FLOWINFO_MASK; | ||||
ip6->ip6_flow |= sc->sc_flowlabel; | ip6->ip6_flow |= sc->sc_flowlabel; | ||||
ip6->ip6_flow |= htonl(sc->sc_ip_tos << 20); | if (sc->sc_port != 0) { | ||||
ip6->ip6_nxt = IPPROTO_UDP; | |||||
udp = (struct udphdr *)(ip6 + 1); | |||||
udp->uh_sport = htons(V_tcp_udp_tunneling_port); | |||||
udp->uh_dport = sc->sc_port; | |||||
ulen = (tlen - sizeof(struct ip6_hdr)); | |||||
th = (struct tcphdr *)(udp + 1); | |||||
} else { | |||||
ip6->ip6_nxt = IPPROTO_TCP; | |||||
th = (struct tcphdr *)(ip6 + 1); | th = (struct tcphdr *)(ip6 + 1); | ||||
} | } | ||||
ip6->ip6_flow |= htonl(sc->sc_ip_tos << 20); | |||||
} | |||||
#endif | #endif | ||||
#if defined(INET6) && defined(INET) | #if defined(INET6) && defined(INET) | ||||
else | else | ||||
#endif | #endif | ||||
#ifdef INET | #ifdef INET | ||||
{ | { | ||||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||||
ip->ip_v = IPVERSION; | ip->ip_v = IPVERSION; | ||||
ip->ip_hl = sizeof(struct ip) >> 2; | ip->ip_hl = sizeof(struct ip) >> 2; | ||||
ip->ip_len = htons(tlen); | ip->ip_len = htons(tlen); | ||||
ip->ip_id = 0; | ip->ip_id = 0; | ||||
ip->ip_off = 0; | ip->ip_off = 0; | ||||
ip->ip_sum = 0; | ip->ip_sum = 0; | ||||
ip->ip_p = IPPROTO_TCP; | |||||
ip->ip_src = sc->sc_inc.inc_laddr; | ip->ip_src = sc->sc_inc.inc_laddr; | ||||
ip->ip_dst = sc->sc_inc.inc_faddr; | ip->ip_dst = sc->sc_inc.inc_faddr; | ||||
ip->ip_ttl = sc->sc_ip_ttl; | ip->ip_ttl = sc->sc_ip_ttl; | ||||
ip->ip_tos = sc->sc_ip_tos; | ip->ip_tos = sc->sc_ip_tos; | ||||
/* | /* | ||||
* See if we should do MTU discovery. Route lookups are | * See if we should do MTU discovery. Route lookups are | ||||
* expensive, so we will only unset the DF bit if: | * expensive, so we will only unset the DF bit if: | ||||
* | * | ||||
* 1) path_mtu_discovery is disabled | * 1) path_mtu_discovery is disabled | ||||
* 2) the SCF_UNREACH flag has been set | * 2) the SCF_UNREACH flag has been set | ||||
*/ | */ | ||||
if (V_path_mtu_discovery && ((sc->sc_flags & SCF_UNREACH) == 0)) | if (V_path_mtu_discovery && ((sc->sc_flags & SCF_UNREACH) == 0)) | ||||
ip->ip_off |= htons(IP_DF); | ip->ip_off |= htons(IP_DF); | ||||
if (sc->sc_port == 0) { | |||||
ip->ip_p = IPPROTO_TCP; | |||||
th = (struct tcphdr *)(ip + 1); | th = (struct tcphdr *)(ip + 1); | ||||
} else { | |||||
ip->ip_p = IPPROTO_UDP; | |||||
udp = (struct udphdr *)(ip + 1); | |||||
udp->uh_sport = htons(V_tcp_udp_tunneling_port); | |||||
udp->uh_dport = sc->sc_port; | |||||
ulen = (tlen - sizeof(struct ip)); | |||||
th = (struct tcphdr *)(udp + 1); | |||||
} | } | ||||
} | |||||
#endif /* INET */ | #endif /* INET */ | ||||
th->th_sport = sc->sc_inc.inc_lport; | th->th_sport = sc->sc_inc.inc_lport; | ||||
th->th_dport = sc->sc_inc.inc_fport; | th->th_dport = sc->sc_inc.inc_fport; | ||||
if (flags & TH_SYN) | if (flags & TH_SYN) | ||||
th->th_seq = htonl(sc->sc_iss); | th->th_seq = htonl(sc->sc_iss); | ||||
else | else | ||||
th->th_seq = htonl(sc->sc_iss + 1); | th->th_seq = htonl(sc->sc_iss + 1); | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (sc->sc_flags & SCF_SIGNATURE) { | ||||
m_freem(m); | m_freem(m); | ||||
return (EACCES); | return (EACCES); | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
} else | } else | ||||
optlen = 0; | optlen = 0; | ||||
if (udp) { | |||||
ulen += optlen; | |||||
udp->uh_ulen = htons(ulen); | |||||
} | |||||
M_SETFIB(m, sc->sc_inc.inc_fibnum); | M_SETFIB(m, sc->sc_inc.inc_fibnum); | ||||
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); | |||||
/* | /* | ||||
* If we have peer's SYN and it has a flowid, then let's assign it to | * If we have peer's SYN and it has a flowid, then let's assign it to | ||||
* our SYN|ACK. ip6_output() and ip_output() will not assign flowid | * our SYN|ACK. ip6_output() and ip_output() will not assign flowid | ||||
* to SYN|ACK due to lack of inp here. | * to SYN|ACK due to lack of inp here. | ||||
*/ | */ | ||||
if (m0 != NULL && M_HASHTYPE_GET(m0) != M_HASHTYPE_NONE) { | if (m0 != NULL && M_HASHTYPE_GET(m0) != M_HASHTYPE_NONE) { | ||||
m->m_pkthdr.flowid = m0->m_pkthdr.flowid; | m->m_pkthdr.flowid = m0->m_pkthdr.flowid; | ||||
M_HASHTYPE_SET(m, M_HASHTYPE_GET(m0)); | M_HASHTYPE_SET(m, M_HASHTYPE_GET(m0)); | ||||
} | } | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if (sc->sc_inc.inc_flags & INC_ISIPV6) { | if (sc->sc_inc.inc_flags & INC_ISIPV6) { | ||||
if (sc->sc_port) { | |||||
m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; | |||||
m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); | |||||
udp->uh_sum = in6_cksum_pseudo(ip6, ulen, | |||||
IPPROTO_UDP, 0); | |||||
th->th_sum = htons(0); | |||||
} else { | |||||
m->m_pkthdr.csum_flags = CSUM_TCP_IPV6; | m->m_pkthdr.csum_flags = CSUM_TCP_IPV6; | ||||
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); | |||||
th->th_sum = in6_cksum_pseudo(ip6, tlen + optlen - hlen, | th->th_sum = in6_cksum_pseudo(ip6, tlen + optlen - hlen, | ||||
IPPROTO_TCP, 0); | IPPROTO_TCP, 0); | ||||
} | |||||
ip6->ip6_hlim = sc->sc_ip_ttl; | ip6->ip6_hlim = sc->sc_ip_ttl; | ||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
if (ADDED_BY_TOE(sc)) { | if (ADDED_BY_TOE(sc)) { | ||||
struct toedev *tod = sc->sc_tod; | struct toedev *tod = sc->sc_tod; | ||||
error = tod->tod_syncache_respond(tod, sc->sc_todctx, m); | error = tod->tod_syncache_respond(tod, sc->sc_todctx, m); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
TCP_PROBE5(send, NULL, NULL, ip6, NULL, th); | TCP_PROBE5(send, NULL, NULL, ip6, NULL, th); | ||||
error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); | error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); | ||||
} | } | ||||
#endif | #endif | ||||
#if defined(INET6) && defined(INET) | #if defined(INET6) && defined(INET) | ||||
else | else | ||||
#endif | #endif | ||||
#ifdef INET | #ifdef INET | ||||
{ | { | ||||
if (sc->sc_port) { | |||||
m->m_pkthdr.csum_flags = CSUM_UDP; | |||||
m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); | |||||
udp->uh_sum = in_pseudo(ip->ip_src.s_addr, | |||||
ip->ip_dst.s_addr, htons(ulen + IPPROTO_UDP)); | |||||
th->th_sum = htons(0); | |||||
} else { | |||||
m->m_pkthdr.csum_flags = CSUM_TCP; | m->m_pkthdr.csum_flags = CSUM_TCP; | ||||
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); | |||||
th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, | th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, | ||||
htons(tlen + optlen - hlen + IPPROTO_TCP)); | htons(tlen + optlen - hlen + IPPROTO_TCP)); | ||||
} | |||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
if (ADDED_BY_TOE(sc)) { | if (ADDED_BY_TOE(sc)) { | ||||
struct toedev *tod = sc->sc_tod; | struct toedev *tod = sc->sc_tod; | ||||
error = tod->tod_syncache_respond(tod, sc->sc_todctx, m); | error = tod->tod_syncache_respond(tod, sc->sc_todctx, m); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | syncookie_generate(struct syncache_head *sch, struct syncache *sc) | ||||
TCPSTAT_INC(tcps_sc_sendcookie); | TCPSTAT_INC(tcps_sc_sendcookie); | ||||
return (iss); | return (iss); | ||||
} | } | ||||
static struct syncache * | static struct syncache * | ||||
syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch, | syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch, | ||||
struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | ||||
struct socket *lso) | struct socket *lso, uint16_t port) | ||||
{ | { | ||||
uint32_t hash; | uint32_t hash; | ||||
uint8_t *secbits; | uint8_t *secbits; | ||||
tcp_seq ack, seq; | tcp_seq ack, seq; | ||||
int wnd, wscale = 0; | int wnd, wscale = 0; | ||||
union syncookie cookie; | union syncookie cookie; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | if (to->to_flags & TOF_TS) { | ||||
sc->sc_tsoff = tcp_new_ts_offset(inc); | sc->sc_tsoff = tcp_new_ts_offset(inc); | ||||
} | } | ||||
if (to->to_flags & TOF_SIGNATURE) | if (to->to_flags & TOF_SIGNATURE) | ||||
sc->sc_flags |= SCF_SIGNATURE; | sc->sc_flags |= SCF_SIGNATURE; | ||||
sc->sc_rxmits = 0; | sc->sc_rxmits = 0; | ||||
sc->sc_port = port; | |||||
TCPSTAT_INC(tcps_sc_recvcookie); | TCPSTAT_INC(tcps_sc_recvcookie); | ||||
return (sc); | return (sc); | ||||
} | } | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
static int | static int | ||||
syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, | syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, | ||||
struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | ||||
struct socket *lso) | struct socket *lso, uint16_t port) | ||||
{ | { | ||||
struct syncache scs, *scx; | struct syncache scs, *scx; | ||||
char *s; | char *s; | ||||
bzero(&scs, sizeof(scs)); | bzero(&scs, sizeof(scs)); | ||||
scx = syncookie_lookup(inc, sch, &scs, th, to, lso); | scx = syncookie_lookup(inc, sch, &scs, th, to, lso, port); | ||||
if ((s = tcp_log_addrs(inc, th, NULL, NULL)) == NULL) | if ((s = tcp_log_addrs(inc, th, NULL, NULL)) == NULL) | ||||
return (0); | return (0); | ||||
if (scx != NULL) { | if (scx != NULL) { | ||||
if (sc->sc_peer_mss != scx->sc_peer_mss) | if (sc->sc_peer_mss != scx->sc_peer_mss) | ||||
log(LOG_DEBUG, "%s; %s: mss different %i vs %i\n", | log(LOG_DEBUG, "%s; %s: mss different %i vs %i\n", | ||||
s, __func__, sc->sc_peer_mss, scx->sc_peer_mss); | s, __func__, sc->sc_peer_mss, scx->sc_peer_mss); | ||||
▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | for (i = 0; i < V_tcp_syncache.hashsize; i++) { | ||||
SCH_LOCK(sch); | SCH_LOCK(sch); | ||||
TAILQ_FOREACH(sc, &sch->sch_bucket, sc_hash) { | TAILQ_FOREACH(sc, &sch->sch_bucket, sc_hash) { | ||||
if (cr_cansee(req->td->td_ucred, sc->sc_cred) != 0) | if (cr_cansee(req->td->td_ucred, sc->sc_cred) != 0) | ||||
continue; | continue; | ||||
if (sc->sc_inc.inc_flags & INC_ISIPV6) | if (sc->sc_inc.inc_flags & INC_ISIPV6) | ||||
xt.xt_inp.inp_vflag = INP_IPV6; | xt.xt_inp.inp_vflag = INP_IPV6; | ||||
else | else | ||||
xt.xt_inp.inp_vflag = INP_IPV4; | xt.xt_inp.inp_vflag = INP_IPV4; | ||||
xt.xt_encaps_port = sc->sc_port; | |||||
bcopy(&sc->sc_inc, &xt.xt_inp.inp_inc, | bcopy(&sc->sc_inc, &xt.xt_inp.inp_inc, | ||||
sizeof (struct in_conninfo)); | sizeof (struct in_conninfo)); | ||||
error = SYSCTL_OUT(req, &xt, sizeof xt); | error = SYSCTL_OUT(req, &xt, sizeof xt); | ||||
if (error) { | if (error) { | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
SCH_UNLOCK(sch); | SCH_UNLOCK(sch); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } |