diff options
-rw-r--r-- | sys/net/pf.c | 147 | ||||
-rw-r--r-- | sys/net/pfvar.h | 3 |
2 files changed, 132 insertions, 18 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 83afd580d05..bdd9d41201b 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.365 2003/06/20 17:38:24 dhartmei Exp $ */ +/* $OpenBSD: pf.c,v 1.366 2003/06/20 18:24:57 dhartmei Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -138,7 +138,7 @@ void pf_change_icmp(struct pf_addr *, u_int16_t *, void pf_send_tcp(const struct pf_rule *, sa_family_t, const struct pf_addr *, const struct pf_addr *, u_int16_t, u_int16_t, u_int32_t, u_int32_t, - u_int8_t, u_int16_t, u_int8_t); + u_int8_t, u_int16_t, u_int16_t, u_int8_t); void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_rule *); struct pf_rule *pf_match_translation(int, struct ifnet *, u_int8_t, @@ -209,6 +209,10 @@ int pf_socket_lookup(uid_t *, gid_t *, int, sa_family_t, int, struct pf_pdesc *); u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); +u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, + sa_family_t); +u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, + u_int16_t); int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); @@ -495,6 +499,7 @@ pf_purge_expired_states(void) cur->state->lan.port, cur->state->src.seqhi, cur->state->src.seqlo + 1, + 0, TH_RST|TH_ACK, 0, 0); RB_REMOVE(pf_state_tree, &tree_ext_gwy, cur); @@ -1079,11 +1084,11 @@ void pf_send_tcp(const struct pf_rule *r, sa_family_t af, const struct pf_addr *saddr, const struct pf_addr *daddr, u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, - u_int8_t flags, u_int16_t win, u_int8_t ttl) + u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl) { struct mbuf *m; struct m_tag *mtag; - int len; + int len, tlen; #ifdef INET struct ip *h; #endif /* INET */ @@ -1091,16 +1096,22 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, struct ip6_hdr *h6; #endif /* INET6 */ struct tcphdr *th; + char *opt; + /* maximum segment size tcp option */ + tlen = sizeof(struct tcphdr); + if (mss) + tlen += 4; + switch (af) { #ifdef INET case AF_INET: - len = sizeof(struct ip) + sizeof(struct tcphdr); + len = sizeof(struct ip) + tlen; break; #endif /* INET */ #ifdef INET6 case AF_INET6: - len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); + len = sizeof(struct ip6_hdr) + tlen; break; #endif /* INET6 */ } @@ -1141,7 +1152,7 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, /* IP header fields included in the TCP checksum */ h->ip_p = IPPROTO_TCP; - h->ip_len = htons(sizeof(*th)); + h->ip_len = htons(tlen); h->ip_src.s_addr = saddr->v4.s_addr; h->ip_dst.s_addr = daddr->v4.s_addr; @@ -1154,7 +1165,7 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, /* IP header fields included in the TCP checksum */ h6->ip6_nxt = IPPROTO_TCP; - h6->ip6_plen = htons(sizeof(*th)); + h6->ip6_plen = htons(tlen); memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr)); memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr)); @@ -1168,10 +1179,18 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, th->th_dport = dport; th->th_seq = htonl(seq); th->th_ack = htonl(ack); - th->th_off = sizeof(*th) >> 2; + th->th_off = tlen >> 2; th->th_flags = flags; th->th_win = htons(win); + if (mss) { + opt = (char *)(th + 1); + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + HTONS(mss); + bcopy((caddr_t)&mss, (caddr_t)(opt + 2), 2); + } + switch (af) { #ifdef INET case AF_INET: @@ -1194,7 +1213,7 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, case AF_INET6: /* TCP checksum */ th->th_sum = in6_cksum(m, IPPROTO_TCP, - sizeof(struct ip6_hdr), sizeof(*th)); + sizeof(struct ip6_hdr), tlen); h6->ip6_vfc |= IPV6_VERSION; h6->ip6_hlim = IPV6_DEFHLIM; @@ -1983,6 +2002,94 @@ pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) return (wscale); } +u_int16_t +pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) +{ + int hlen; + u_int8_t hdr[60]; + u_int8_t *opt, optlen; + u_int16_t mss = tcp_mssdflt; + + hlen = th_off << 2; /* hlen <= sizeof(hdr) */ + if (hlen <= sizeof(struct tcphdr)) + return (0); + if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) + return (0); + opt = hdr + sizeof(struct tcphdr); + hlen -= sizeof(struct tcphdr); + while (hlen >= TCPOLEN_MAXSEG) { + switch (*opt) { + case TCPOPT_EOL: + case TCPOPT_NOP: + ++opt; + --hlen; + break; + case TCPOPT_MAXSEG: + bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); + /* fallthrough */ + default: + optlen = opt[1]; + if (optlen < 2) + optlen = 2; + hlen -= optlen; + opt += optlen; + } + } + return (mss); +} + +u_int16_t +pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) +{ +#ifdef INET + struct sockaddr_in *dst; + struct route ro; +#endif /* INET */ +#ifdef INET6 + struct sockaddr_in6 *dst6; + struct route_in6 ro6; +#endif /* INET6 */ + struct rtentry *rt = NULL; + int hlen; + u_int16_t mss = tcp_mssdflt; + + switch (af) { +#ifdef INET + case AF_INET: + hlen = sizeof(struct ip); + bzero(&ro, sizeof(ro)); + dst = (struct sockaddr_in *)&ro.ro_dst; + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = addr->v4; + rtalloc_noclone(&ro, NO_CLONING); + rt = ro.ro_rt; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + hlen = sizeof(struct ip6_hdr); + bzero(&ro6, sizeof(ro6)); + dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(*dst6); + dst6->sin6_addr = addr->v6; + rtalloc_noclone((struct route *)&ro6, NO_CLONING); + rt = ro6.ro_rt; + break; +#endif /* INET6 */ + } + + if (rt && rt->rt_ifp) { + mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); + mss = max(tcp_mssdflt, mss); + RTFREE(rt); + } + mss = min(mss, offer); + mss = max(mss, 64); /* sanity - at least max opt space */ + return (mss); +} + int pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, @@ -2003,6 +2110,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, int rewrite = 0; struct pf_tag *pftag = NULL; int tag = -1; + u_int16_t mss = tcp_mssdflt; if (direction == PF_OUT) { bport = nport = th->th_sport; @@ -2134,7 +2242,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, ack++; pf_send_tcp(r, af, pd->dst, pd->src, th->th_dport, th->th_sport, - ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, + ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, r->return_ttl); } else if ((af == AF_INET) && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, @@ -2279,9 +2387,14 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, pd->ip_sum, &th->th_sum, &baddr, bport, 0, af); s->src.seqhi = arc4random(); + /* Find mss option */ + mss = pf_get_mss(m, off, th->th_off, af); + mss = pf_calc_mss(saddr, af, mss); + mss = pf_calc_mss(daddr, af, mss); + s->src.mss = mss; pf_send_tcp(r, af, daddr, saddr, th->th_dport, th->th_sport, s->src.seqhi, - ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, 0); + ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, s->src.mss, 0); return (PF_SYNPROXY_DROP); } } @@ -3126,7 +3239,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, (*state)->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, 0); + TH_SYN|TH_ACK, 0, (*state)->src.mss, 0); return (PF_SYNPROXY_DROP); } else if (!(th->th_flags & TH_ACK) || (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || @@ -3155,7 +3268,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, (*state)->dst.seqhi = arc4random(); pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, &dst->addr, src->port, dst->port, - (*state)->dst.seqhi, 0, TH_SYN, 0, 0); + (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0); return (PF_SYNPROXY_DROP); } else if (((th->th_flags & (TH_SYN|TH_ACK)) != (TH_SYN|TH_ACK)) || @@ -3167,11 +3280,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ntohl(th->th_seq) + 1, - TH_ACK, (*state)->src.max_win, 0); + TH_ACK, (*state)->src.max_win, 0, 0); pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, &dst->addr, src->port, dst->port, (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, - TH_ACK, (*state)->dst.max_win, 0); + TH_ACK, (*state)->dst.max_win, 0, 0); (*state)->src.seqdiff = (*state)->dst.seqhi - (*state)->src.seqlo; (*state)->dst.seqdiff = (*state)->src.seqhi - @@ -3438,7 +3551,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ack, - TH_RST|TH_ACK, 0, + TH_RST|TH_ACK, 0, 0, (*state)->rule.ptr->return_ttl); } src->seqlo = 0; diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 746fd7151b5..5bea9368633 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.155 2003/06/09 11:14:46 mcbride Exp $ */ +/* $OpenBSD: pfvar.h,v 1.156 2003/06/20 18:24:57 dhartmei Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -441,6 +441,7 @@ struct pf_state_peer { u_int16_t max_win; /* largest window (pre scaling) */ u_int8_t state; /* active state level */ u_int8_t wscale; /* window scaling factor */ + u_int16_t mss; /* Maximum segment size option */ struct pf_state_scrub *scrub; /* state is scrubbed */ }; |