static int sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp) { sctp_sctphdr_t *sctph; unsigned int sctphoff; struct sk_buff *iter; __be32 crc32; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) sctphoff = sizeof(struct ipv6hdr); else #endif sctphoff = ip_hdrlen(skb); /* csum_check requires unshared skb */ if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) return 0; if (unlikely(cp->app != NULL)) { /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ if (!ip_vs_app_pkt_out(cp, skb)) return 0; } sctph = (void *) skb_network_header(skb) + sctphoff; sctph->source = cp->vport; /* Calculate the checksum */ crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff); skb_walk_frags(skb, iter) crc32 = sctp_update_cksum((u8 *) iter->data, skb_headlen(iter), crc32); crc32 = sctp_end_cksum(crc32); sctph->checksum = crc32; return 1; }
static bool sctp_manip_pkt(struct sk_buff *skb, unsigned int iphdroff, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); sctp_sctphdr_t *hdr; unsigned int hdroff = iphdroff + iph->ihl*4; __be32 oldip, newip; u32 crc32; if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) return false; iph = (struct iphdr *)(skb->data + iphdroff); hdr = (struct sctphdr *)(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; hdr->source = tuple->src.u.sctp.port; } else { /* Get rid of dst ip and dst pt */ oldip = iph->daddr; newip = tuple->dst.u3.ip; hdr->dest = tuple->dst.u.sctp.port; } crc32 = sctp_start_cksum((u8 *)hdr, skb_headlen(skb) - hdroff); for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) crc32 = sctp_update_cksum((u8 *)skb->data, skb_headlen(skb), crc32); crc32 = sctp_end_cksum(crc32); hdr->checksum = htonl(crc32); return true; }
static int sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) { unsigned int sctphoff; struct sctphdr *sh, _sctph; struct sk_buff *iter; __le32 cmp; __le32 val; __u32 tmp; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) sctphoff = sizeof(struct ipv6hdr); else #endif sctphoff = ip_hdrlen(skb); sh = skb_header_pointer(skb, sctphoff, sizeof(_sctph), &_sctph); if (sh == NULL) return 0; cmp = sh->checksum; tmp = sctp_start_cksum((__u8 *) sh, skb_headlen(skb)); skb_walk_frags(skb, iter) tmp = sctp_update_cksum((__u8 *) iter->data, skb_headlen(iter), tmp); val = sctp_end_cksum(tmp); if (val != cmp) { /* CRC failure, dump it. */ IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, "Failed checksum for"); return 0; } return 1; }