/* Return 0 if there was an error. */ static inline int set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) { struct tcphdr _tcph, *tcph; __be16 oldval; /* Not enought header? */ tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph), &_tcph); if (!tcph) return 0; if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) || tcph->ece == einfo->proto.tcp.ece) && ((!(einfo->operation & IPT_ECN_OP_SET_CWR) || tcph->cwr == einfo->proto.tcp.cwr))) return 1; if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph))) return 0; tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4; oldval = ((__be16 *)tcph)[6]; if (einfo->operation & IPT_ECN_OP_SET_ECE) tcph->ece = einfo->proto.tcp.ece; if (einfo->operation & IPT_ECN_OP_SET_CWR) tcph->cwr = einfo->proto.tcp.cwr; nf_proto_csum_replace2(&tcph->check, *pskb, oldval, ((__be16 *)tcph)[6], 0); return 1; }
/* Return false if there was an error. */ static inline bool set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) { struct tcphdr _tcph, *tcph; __be16 oldval; /* Not enought header? */ tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); if (!tcph) return false; if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) || tcph->ece == einfo->proto.tcp.ece) && (!(einfo->operation & IPT_ECN_OP_SET_CWR) || tcph->cwr == einfo->proto.tcp.cwr)) return true; if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) return false; tcph = (void *)ip_hdr(skb) + ip_hdrlen(skb); oldval = ((__be16 *)tcph)[6]; if (einfo->operation & IPT_ECN_OP_SET_ECE) tcph->ece = einfo->proto.tcp.ece; if (einfo->operation & IPT_ECN_OP_SET_CWR) tcph->cwr = einfo->proto.tcp.cwr; nf_proto_csum_replace2(&tcph->check, skb, oldval, ((__be16 *)tcph)[6], 0); return true; }
/* Generic function for mangling variable-length address changes inside * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX * command in the Amanda protocol) * * Takes care about all the nasty sequence number changes, checksumming, * skb enlargement, ... * * XXX - This function could be merged with nf_nat_mangle_tcp_packet which * should be fairly easy to do. */ int nf_nat_mangle_udp_packet(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct udphdr *udph; int datalen, oldlen; /* UDP helpers might accidentally mangle the wrong packet */ iph = (*pskb)->nh.iph; if ((*pskb)->len < iph->ihl*4 + sizeof(*udph) + match_offset + match_len) return 0; if (!skb_make_writable(pskb, (*pskb)->len)) return 0; if (rep_len > match_len && rep_len - match_len > skb_tailroom(*pskb) && !enlarge_skb(pskb, rep_len - match_len)) return 0; iph = (*pskb)->nh.iph; udph = (void *)iph + iph->ihl*4; oldlen = (*pskb)->len - iph->ihl*4; mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), match_offset, match_len, rep_buffer, rep_len); /* update the length of the UDP packet */ datalen = (*pskb)->len - iph->ihl*4; udph->len = htons(datalen); /* fix udp checksum if udp checksum was previously calculated */ if (!udph->check && (*pskb)->ip_summed != CHECKSUM_PARTIAL) return 1; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { udph->check = 0; udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP, csum_partial((char *)udph, datalen, 0)); if (!udph->check) udph->check = CSUM_MANGLED_0; } else nf_proto_csum_replace2(&udph->check, *pskb, htons(oldlen), htons(datalen), 1); return 1; }
/* Generic function for mangling variable-length address changes inside * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX * command in FTP). * * Takes care about all the nasty sequence number changes, checksumming, * skb enlargement, ... * * */ int nf_nat_mangle_tcp_packet(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct tcphdr *tcph; int oldlen, datalen; if (!skb_make_writable(pskb, (*pskb)->len)) return 0; if (rep_len > match_len && rep_len - match_len > skb_tailroom(*pskb) && !enlarge_skb(pskb, rep_len - match_len)) return 0; SKB_LINEAR_ASSERT(*pskb); iph = (*pskb)->nh.iph; tcph = (void *)iph + iph->ihl*4; oldlen = (*pskb)->len - iph->ihl*4; mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, match_offset, match_len, rep_buffer, rep_len); datalen = (*pskb)->len - iph->ihl*4; if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { tcph->check = 0; tcph->check = tcp_v4_check(datalen, iph->saddr, iph->daddr, csum_partial((char *)tcph, datalen, 0)); } else nf_proto_csum_replace2(&tcph->check, *pskb, htons(oldlen), htons(datalen), 1); if (rep_len != match_len) { set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); adjust_tcp_sequence(ntohl(tcph->seq), (int)rep_len - (int)match_len, ct, ctinfo); /* Tell TCP window tracking about seq change */ nf_conntrack_tcp_update(*pskb, (*pskb)->nh.iph->ihl*4, ct, CTINFO2DIR(ctinfo)); } return 1; }
static int tcp_manip_pkt(struct sk_buff *skb, unsigned int iphdroff, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); struct tcphdr *hdr; unsigned int hdroff = iphdroff + iph->ihl*4; __be32 oldip, newip; __be16 *portptr, newport, oldport; int hdrsize = 8; /* TCP connection tracking guarantees this much */ /* this could be a inner header returned in icmp packet; in such cases we cannot update the checksum field since it is outside of the 8 bytes of transport layer headers we are guaranteed */ if (skb->len >= hdroff + sizeof(struct tcphdr)) hdrsize = sizeof(struct tcphdr); if (!skb_make_writable(skb, hdroff + hdrsize)) return 0; iph = (struct iphdr *)(skb->data + iphdroff); hdr = (struct tcphdr *)(skb->data + hdroff); if (maniptype == IP_NAT_MANIP_SRC) { /* Get rid of src ip and src pt */ oldip = iph->saddr; newip = tuple->src.u3.ip; newport = tuple->src.u.tcp.port; portptr = &hdr->source; } else { /* Get rid of dst ip and dst pt */ oldip = iph->daddr; newip = tuple->dst.u3.ip; newport = tuple->dst.u.tcp.port; portptr = &hdr->dest; } oldport = *portptr; *portptr = newport; if (hdrsize < sizeof(*hdr)) return 1; nf_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); nf_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); return 1; }
static int icmp_manip_pkt(struct sk_buff **pskb, unsigned int iphdroff, const struct ip_conntrack_tuple *tuple, enum ip_nat_manip_type maniptype) { struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); struct icmphdr *hdr; unsigned int hdroff = iphdroff + iph->ihl*4; if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) return 0; hdr = (struct icmphdr *)((*pskb)->data + hdroff); nf_proto_csum_replace2(&hdr->checksum, *pskb, hdr->un.echo.id, tuple->src.u.icmp.id, 0); hdr->un.echo.id = tuple->src.u.icmp.id; return 1; }
static int udp_manip_pkt(struct sk_buff **pskb, unsigned int iphdroff, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); struct udphdr *hdr; unsigned int hdroff = iphdroff + iph->ihl*4; __be32 oldip, newip; __be16 *portptr, newport; if (!skb_make_writable(pskb, hdroff + sizeof(*hdr))) return 0; iph = (struct iphdr *)((*pskb)->data + iphdroff); hdr = (struct udphdr *)((*pskb)->data + hdroff); if (maniptype == IP_NAT_MANIP_SRC) { /* Get rid of src ip and src pt */ oldip = iph->saddr; newip = tuple->src.u3.ip; newport = tuple->src.u.udp.port; portptr = &hdr->source; } else { /* Get rid of dst ip and dst pt */ oldip = iph->daddr; newip = tuple->dst.u3.ip; newport = tuple->dst.u.udp.port; portptr = &hdr->dest; } if (hdr->check || (*pskb)->ip_summed == CHECKSUM_PARTIAL) { nf_proto_csum_replace4(&hdr->check, *pskb, oldip, newip, 1); nf_proto_csum_replace2(&hdr->check, *pskb, *portptr, newport, 0); if (!hdr->check) hdr->check = CSUM_MANGLED_0; } *portptr = newport; return 1; }
static int tcpmss_mangle_packet(struct sk_buff **pskb, const struct xt_tcpmss_info *info, unsigned int tcphoff, unsigned int minlen) { struct tcphdr *tcph; unsigned int tcplen, i; __be16 oldval; u16 newmss; u8 *opt; if (!skb_make_writable(pskb, (*pskb)->len)) return -1; tcplen = (*pskb)->len - tcphoff; tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff); /* Since it passed flags test in tcp match, we know it is is not a fragment, and has data >= tcp header length. SYN packets should not contain data: if they did, then we risk running over MTU, sending Frag Needed and breaking things badly. --RR */ if (tcplen != tcph->doff*4) { if (net_ratelimit()) printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n", (*pskb)->len); return -1; } if (info->mss == XT_TCPMSS_CLAMP_PMTU) { if (dst_mtu((*pskb)->dst) <= minlen) { if (net_ratelimit()) printk(KERN_ERR "xt_TCPMSS: " "unknown or invalid path-MTU (%u)\n", dst_mtu((*pskb)->dst)); return -1; } newmss = dst_mtu((*pskb)->dst) - minlen; } else newmss = info->mss; opt = (u_int8_t *)tcph; for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && opt[i+1] == TCPOLEN_MSS) { u_int16_t oldmss; oldmss = (opt[i+2] << 8) | opt[i+3]; if (info->mss == XT_TCPMSS_CLAMP_PMTU && oldmss <= newmss) return 0; opt[i+2] = (newmss & 0xff00) >> 8; opt[i+3] = newmss & 0x00ff; nf_proto_csum_replace2(&tcph->check, *pskb, htons(oldmss), htons(newmss), 0); return 0; } }