return 0;
}
+static int
+__snat_this_address(struct snat_conf *sc, u32 ip_addr)
+{
+ if (sc) {
+ u32 h_ip_addr = ntohl(ip_addr);
+ return (h_ip_addr >= sc->ip_addr_start &&
+ h_ip_addr <= sc->ip_addr_end);
+ }
+ return 0;
+}
+
+static int
+snat_this_address(struct net_bridge_port *p, u32 ip_addr)
+{
+ unsigned long int flags;
+ int retval;
+
+ spin_lock_irqsave(&p->lock, flags);
+ retval = __snat_this_address(p->snat, ip_addr);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return retval;
+}
+
static int
snat_pre_route_finish(struct sk_buff *skb)
{
struct net_bridge_port *p = skb->dev->br_port;
struct snat_conf *sc;
struct iphdr *iph = ip_hdr(skb);
- uint32_t ip_addr;
unsigned long flags;
skb->dst = (struct dst_entry *)&__fake_rtable;
/* Don't process packets that were not translated due to NAT */
spin_lock_irqsave(&p->lock, flags);
sc = p->snat;
- ip_addr = ntohl(iph->daddr);
- if (sc && (ip_addr >= sc->ip_addr_start)
- && (ip_addr <= sc->ip_addr_end)) {
+ if (__snat_this_address(sc, iph->daddr)) {
spin_unlock_irqrestore(&p->lock, flags);
return -1;
}
handle_arp_snat(struct sk_buff *skb)
{
struct net_bridge_port *p = skb->dev->br_port;
- struct ip_arphdr *ah = (struct ip_arphdr *)arp_hdr(skb);
- uint32_t ip_addr;
- unsigned long flags;
- struct snat_conf *sc;
+ struct ip_arphdr *ah;
+ if (!pskb_may_pull(skb, sizeof *ah))
+ return 0;
+
+ ah = (struct ip_arphdr *)arp_hdr(skb);
if ((ah->ar_op != htons(ARPOP_REQUEST))
|| ah->ar_hln != ETH_ALEN
|| ah->ar_pro != htons(ETH_P_IP)
|| ah->ar_pln != 4)
return 0;
- ip_addr = ntohl(ah->ar_tip);
- spin_lock_irqsave(&p->lock, flags);
- sc = p->snat;
-
/* We're only interested in addresses we rewrite. */
- if (!sc || (sc && ((ip_addr < sc->ip_addr_start)
- || (ip_addr > sc->ip_addr_end)))) {
- spin_unlock_irqrestore(&p->lock, flags);
+ if (!snat_this_address(p, ah->ar_tip)) {
return 0;
}
- spin_unlock_irqrestore(&p->lock, flags);
arp_send(ARPOP_REPLY, ETH_P_ARP, ah->ar_sip, skb->dev, ah->ar_tip,
ah->ar_sha, p->dp->netdev->dev_addr, ah->ar_sha);
handle_icmp_snat(struct sk_buff *skb)
{
struct net_bridge_port *p = skb->dev->br_port;
- struct snat_conf *sc;
struct ethhdr *eh;
- struct iphdr *iph = ip_hdr(skb);
- uint32_t ip_addr;
+ struct iphdr *iph;
struct icmphdr *icmph;
- unsigned int datalen;
uint8_t tmp_eth[ETH_ALEN];
uint32_t tmp_ip;
struct sk_buff *nskb;
- unsigned long flags;
-
-
- ip_addr = ntohl(iph->daddr);
- spin_lock_irqsave(&p->lock, flags);
- sc = p->snat;
/* We're only interested in addresses we rewrite. */
- if (!sc || (sc && ((ip_addr < sc->ip_addr_start)
- || (ip_addr > sc->ip_addr_end)))) {
- spin_unlock_irqrestore(&p->lock, flags);
+ iph = ip_hdr(skb);
+ if (!snat_this_address(p, iph->daddr)) {
return 0;
}
- spin_unlock_irqrestore(&p->lock, flags);
-
- icmph = (struct icmphdr *) ((u_int32_t *)iph + iph->ihl);
- datalen = skb->len - iph->ihl * 4;
/* Drop fragments and packets not long enough to hold the ICMP
* header. */
- if (((ntohs(iph->frag_off) & IP_OFFSET) != 0) || datalen < 4)
+ if ((ntohs(iph->frag_off) & IP_OFFSET) != 0 ||
+ !pskb_may_pull(skb, skb_transport_offset(skb) + 4))
return 0;
/* We only respond to echo requests to our address. Continue
* processing replies and other ICMP messages since they may be
* intended for NAT'd hosts. */
+ icmph = icmp_hdr(skb);
if (icmph->type != ICMP_ECHO)
return 0;
return -1;
}
+ /* Update Ethernet header. */
eh = eth_hdr(nskb);
- iph = ip_hdr(nskb);
- icmph = (struct icmphdr *) ((u_int32_t *)iph + iph->ihl);
+ memcpy(tmp_eth, eh->h_dest, ETH_ALEN);
+ memcpy(eh->h_dest, eh->h_source, ETH_ALEN);
+ memcpy(eh->h_source, tmp_eth, ETH_ALEN);
+ /* Update IP header.
+ * This is kind of busted, at least in that it doesn't check that the
+ * echoed IP options make sense. */
+ iph = ip_hdr(nskb);
+ iph->id = 0;
+ iph->frag_off = 0;
+ iph->ttl = IPDEFTTL;
+ iph->check = 0;
tmp_ip = iph->daddr;
iph->daddr = iph->saddr;
iph->saddr = tmp_ip;
+ iph->check = ip_fast_csum(iph, iph->ihl);
- memcpy(tmp_eth, eh->h_dest, ETH_ALEN);
- memcpy(eh->h_dest, eh->h_source, ETH_ALEN);
- memcpy(eh->h_source, tmp_eth, ETH_ALEN);
-
+ /* Update ICMP header. */
+ icmph = icmp_hdr(nskb);
icmph->type = ICMP_ECHOREPLY;
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum(icmph,
+ nskb->tail - nskb->transport_header);
dp_xmit_skb_push(nskb);
struct iphdr *iph;
int len;
+ WARN_ON_ONCE(skb_network_offset(skb));
if (skb->protocol == htons(ETH_P_ARP))
return handle_arp_snat(skb);
else if (skb->protocol != htons(ETH_P_IP))
return 0;
+ if (!pskb_may_pull(skb, sizeof *iph))
+ goto ipv4_error;
+
iph = ip_hdr(skb);
if (iph->ihl < 5 || iph->version != 4)
goto ipv4_error;
- if (!pskb_may_pull(skb, iph->ihl*4))
+ if (!pskb_may_pull(skb, ip_hdrlen(skb)))
goto ipv4_error;
+ skb_set_transport_header(skb, ip_hdrlen(skb));
/* Check if we need to echo reply for this address */
+ iph = ip_hdr(skb);
if ((iph->protocol == IPPROTO_ICMP) && (handle_icmp_snat(skb)))
return -1;
- if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
+ iph = ip_hdr(skb);
+ if (unlikely(ip_fast_csum(iph, iph->ihl)))
goto ipv4_error;
len = ntohs(iph->tot_len);
list_for_each_entry (m, &sc->mappings, node) {
if (m->ip_addr == iph->saddr){
- if (memcmp(m->hw_addr, eh->h_source, ETH_ALEN)) {
- memcpy(m->hw_addr, eh->h_source, ETH_ALEN);
- }
+ memcpy(m->hw_addr, eh->h_source, ETH_ALEN);
m->used = jiffies;
goto done;
}
if (!p)
return;
+ /* FIXME: Expensive. Just need to skb_clone() here?
+ * (However, the skb_copy() does linearize and ensure that the headers
+ * are accessible.) */
nskb = skb_copy(skb, GFP_ATOMIC);
if (!nskb)
return;