summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net/pf.c147
-rw-r--r--sys/net/pfvar.h3
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 */
};