netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one
[platform/kernel/linux-starfive.git] / net / netfilter / nf_conntrack_proto_dccp.c
index c1557d4..d4fd626 100644 (file)
@@ -432,9 +432,19 @@ static bool dccp_error(const struct dccp_hdr *dh,
                       struct sk_buff *skb, unsigned int dataoff,
                       const struct nf_hook_state *state)
 {
+       static const unsigned long require_seq48 = 1 << DCCP_PKT_REQUEST |
+                                                  1 << DCCP_PKT_RESPONSE |
+                                                  1 << DCCP_PKT_CLOSEREQ |
+                                                  1 << DCCP_PKT_CLOSE |
+                                                  1 << DCCP_PKT_RESET |
+                                                  1 << DCCP_PKT_SYNC |
+                                                  1 << DCCP_PKT_SYNCACK;
        unsigned int dccp_len = skb->len - dataoff;
        unsigned int cscov;
        const char *msg;
+       u8 type;
+
+       BUILD_BUG_ON(DCCP_PKT_INVALID >= BITS_PER_LONG);
 
        if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) ||
            dh->dccph_doff * 4 > dccp_len) {
@@ -459,34 +469,70 @@ static bool dccp_error(const struct dccp_hdr *dh,
                goto out_invalid;
        }
 
-       if (dh->dccph_type >= DCCP_PKT_INVALID) {
+       type = dh->dccph_type;
+       if (type >= DCCP_PKT_INVALID) {
                msg = "nf_ct_dccp: reserved packet type ";
                goto out_invalid;
        }
+
+       if (test_bit(type, &require_seq48) && !dh->dccph_x) {
+               msg = "nf_ct_dccp: type lacks 48bit sequence numbers";
+               goto out_invalid;
+       }
+
        return false;
 out_invalid:
        nf_l4proto_log_invalid(skb, state, IPPROTO_DCCP, "%s", msg);
        return true;
 }
 
+struct nf_conntrack_dccp_buf {
+       struct dccp_hdr dh;      /* generic header part */
+       struct dccp_hdr_ext ext; /* optional depending dh->dccph_x */
+       union {                  /* depends on header type */
+               struct dccp_hdr_ack_bits ack;
+               struct dccp_hdr_request req;
+               struct dccp_hdr_response response;
+               struct dccp_hdr_reset rst;
+       } u;
+};
+
+static struct dccp_hdr *
+dccp_header_pointer(const struct sk_buff *skb, int offset, const struct dccp_hdr *dh,
+                   struct nf_conntrack_dccp_buf *buf)
+{
+       unsigned int hdrlen = __dccp_hdr_len(dh);
+
+       if (hdrlen > sizeof(*buf))
+               return NULL;
+
+       return skb_header_pointer(skb, offset, hdrlen, buf);
+}
+
 int nf_conntrack_dccp_packet(struct nf_conn *ct, struct sk_buff *skb,
                             unsigned int dataoff,
                             enum ip_conntrack_info ctinfo,
                             const struct nf_hook_state *state)
 {
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-       struct dccp_hdr _dh, *dh;
+       struct nf_conntrack_dccp_buf _dh;
        u_int8_t type, old_state, new_state;
        enum ct_dccp_roles role;
        unsigned int *timeouts;
+       struct dccp_hdr *dh;
 
-       dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+       dh = skb_header_pointer(skb, dataoff, sizeof(*dh), &_dh.dh);
        if (!dh)
                return NF_DROP;
 
        if (dccp_error(dh, skb, dataoff, state))
                return -NF_ACCEPT;
 
+       /* pull again, including possible 48 bit sequences and subtype header */
+       dh = dccp_header_pointer(skb, dataoff, dh, &_dh);
+       if (!dh)
+               return NF_DROP;
+
        type = dh->dccph_type;
        if (!nf_ct_is_confirmed(ct) && !dccp_new(ct, skb, dh, state))
                return -NF_ACCEPT;