void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, const struct in6_addr *gw, int oif) { if (this_cpu_read(nf_skb_duplicated)) return; skb = pskb_copy(skb, GFP_ATOMIC); if (skb == NULL) return; #if IS_ENABLED(CONFIG_NF_CONNTRACK) nf_reset(skb); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); #endif if (hooknum == NF_INET_PRE_ROUTING || hooknum == NF_INET_LOCAL_IN) { struct ipv6hdr *iph = ipv6_hdr(skb); --iph->hop_limit; } if (nf_dup_ipv6_route(net, skb, gw, oif)) { __this_cpu_write(nf_skb_duplicated, true); ip6_local_out(net, skb->sk, skb); __this_cpu_write(nf_skb_duplicated, false); } else { kfree_skb(skb); } }
static unsigned int tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tee_tginfo *info = par->targinfo; if (percpu_read(tee_active)) return XT_CONTINUE; skb = pskb_copy(skb, GFP_ATOMIC); if (skb == NULL) return XT_CONTINUE; #ifdef WITH_CONNTRACK nf_conntrack_put(skb->nfct); skb->nfct = &nf_ct_untracked_get()->ct_general; skb->nfctinfo = IP_CT_NEW; nf_conntrack_get(skb->nfct); #endif if (par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_LOCAL_IN) { struct ipv6hdr *iph = ipv6_hdr(skb); --iph->hop_limit; } if (tee_tg_route6(skb, info)) { percpu_write(tee_active, true); ip6_local_out(skb); percpu_write(tee_active, false); } else { kfree_skb(skb); } return XT_CONTINUE; }
bool send_packet_ipv6(struct sk_buff *skb_in, struct sk_buff *skb_out) { struct dst_entry *dst; int error; skb_out->protocol = htons(ETH_P_IPV6); dst = route_packet_ipv6(skb_out); if (!dst) { kfree_skb(skb_out); return false; } skb_out->dev = dst->dev; skb_dst_set(skb_out, dst); if (skb_in) { ipv6_mtu_hack(skb_in, skb_out); if (!ipv6_validate_packet_len(skb_in, skb_out)) { kfree_skb(skb_out); return false; } } log_debug("Sending packet via device '%s'...", skb_out->dev->name); error = ip6_local_out(skb_out); /* Send. */ if (error) { log_err(ERR_SEND_FAILED, "ip6_local_out() failed. Code: %d. Cannot send packet.", error); return false; } return true; }
int rxe_send(struct rxe_pkt_info *pkt, struct sk_buff *skb) { int err; skb->destructor = rxe_skb_tx_dtor; skb->sk = pkt->qp->sk->sk; rxe_add_ref(pkt->qp); atomic_inc(&pkt->qp->skb_out); if (skb->protocol == htons(ETH_P_IP)) { err = ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); } else if (skb->protocol == htons(ETH_P_IPV6)) { err = ip6_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); } else { pr_err("Unknown layer 3 protocol: %d\n", skb->protocol); atomic_dec(&pkt->qp->skb_out); rxe_drop_ref(pkt->qp); kfree_skb(skb); return -EINVAL; } if (unlikely(net_xmit_eval(err))) { pr_debug("error sending packet: %d\n", err); return -EAGAIN; } return 0; }
void rpl_ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, struct net_device *dev) { int pkt_len, err; pkt_len = skb->len - skb_inner_network_offset(skb); #ifdef HAVE_IP6_LOCAL_OUT_SK err = ip6_local_out_sk(sk, skb); #else err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb); #endif if (net_xmit_eval(err)) pkt_len = -1; iptunnel_xmit_stats(dev, pkt_len); }
int ip6_send_skb(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); int err; err = ip6_local_out(net, skb->sk, skb); if (err) { if (err > 0) err = net_xmit_errno(err); if (err) IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); } return err; }
static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, struct net_device *dev) { const struct ipv6hdr *iph = ipv6_hdr(skb); struct net *net = dev_net(skb->dev); struct flowi6 fl6 = { /* needed to match OIF rule */ .flowi6_oif = dev->ifindex, .flowi6_iif = LOOPBACK_IFINDEX, .daddr = iph->daddr, .saddr = iph->saddr, .flowlabel = ip6_flowinfo(iph), .flowi6_mark = skb->mark, .flowi6_proto = iph->nexthdr, .flowi6_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF, }; int ret = NET_XMIT_DROP; struct dst_entry *dst; struct dst_entry *dst_null = &net->ipv6.ip6_null_entry->dst; dst = ip6_route_output(net, NULL, &fl6); if (dst == dst_null) goto err; skb_dst_drop(skb); skb_dst_set(skb, dst); ret = ip6_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(ret))) dev->stats.tx_errors++; else ret = NET_XMIT_SUCCESS; return ret; err: vrf_tx_error(dev, skb); return NET_XMIT_DROP; } #else static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, struct net_device *dev) { vrf_tx_error(dev, skb); return NET_XMIT_DROP; }
static void synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb, struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, struct ipv6hdr *niph, struct tcphdr *nth, unsigned int tcp_hdr_size) { struct net *net = nf_ct_net((struct nf_conn *)nfct); struct dst_entry *dst; struct flowi6 fl6; nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0); nskb->ip_summed = CHECKSUM_PARTIAL; nskb->csum_start = (unsigned char *)nth - nskb->head; nskb->csum_offset = offsetof(struct tcphdr, check); memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; fl6.saddr = niph->saddr; fl6.daddr = niph->daddr; fl6.fl6_sport = nth->source; fl6.fl6_dport = nth->dest; security_skb_classify_flow((struct sk_buff *)skb, flowi6_to_flowi(&fl6)); dst = ip6_route_output(net, NULL, &fl6); if (dst == NULL || dst->error) { dst_release(dst); goto free_nskb; } dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); if (IS_ERR(dst)) goto free_nskb; skb_dst_set(nskb, dst); if (nfct) { nskb->nfct = nfct; nskb->nfctinfo = ctinfo; nf_conntrack_get(nfct); } ip6_local_out(nskb); return; free_nskb: kfree_skb(nskb); }
int nat64_send_packet_ipv6(struct sk_buff *skb) { // Function based on Ecdysis's nat64_output_ipv4 struct ipv6hdr *iph = ipv6_hdr(skb); struct flowi fl; struct dst_entry *dst; //union nat64_l4header_t { // struct udphdr * uh; // struct tcphdr * th; // struct icmp6hdr * icmph; //} l4header; //int i = 0; skb->protocol = htons(ETH_P_IPV6); memset(&fl, 0, sizeof(fl)); fl.u.ip6.saddr = iph->saddr; fl.u.ip6.daddr = iph->daddr; fl.u.ip6.flowlabel = 0; fl.flowi_proto= skb->protocol; dst = ip6_route_output(&init_net, NULL, &fl.u.ip6); if (!dst) { pr_warning("error: ip6_route_output failed."); return -EINVAL; } skb->dev = dst->dev; skb_dst_set(skb, dst); /* * Makes sure the net_device can actually send packets. */ netif_start_queue(skb->dev); if(ip6_local_out(skb)) { pr_warning("nf_NAT64: ip6_local_out failed"); return -EINVAL; } return 0; }
int nat64_send_packet_ipv6(struct sk_buff *skb) { // Function based on Ecdysis's nat64_output_ipv4 struct ipv6hdr *iph = ipv6_hdr(skb); struct flowi fl; struct dst_entry *dst; skb->protocol = htons(ETH_P_IPV6); memset(&fl, 0, sizeof(fl)); if(!&(fl.fl6_src)) { return -EINVAL; } fl.fl6_src = iph->saddr; fl.fl6_dst = iph->daddr; fl.fl6_flowlabel = 0; fl.proto = skb->protocol; dst = ip6_route_output(&init_net, NULL, &fl); if (!dst) { pr_warning("error: ip6_route_output failed"); return -EINVAL; } skb->dev = dst->dev; skb_dst_set(skb, dst); if(ip6_local_out(skb)) { pr_warning("nf_NAT64: ip6_local_out failed."); return -EINVAL; } return 0; }
int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { struct rt6_info *rt; /* Route to the other host */ struct in6_addr saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ struct ipv6hdr *old_iph = ipv6_hdr(skb); struct ipv6hdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ int ret, local; EnterFunction(10); rcu_read_lock(); local = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, &saddr, ipvsh, 1, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_TUNNEL); if (local < 0) goto tx_error; if (local) { rcu_read_unlock(); return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); } rt = (struct rt6_info *) skb_dst(skb); tdev = rt->dst.dev; /* * Okay, now see if we can stuff it in the buffer as-is. */ max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr); if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) { struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) goto tx_error; consume_skb(skb); skb = new_skb; old_iph = ipv6_hdr(skb); } skb->transport_header = skb->network_header; skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* * Push down and install the IPIP header. */ iph = ipv6_hdr(skb); iph->version = 6; iph->nexthdr = IPPROTO_IPV6; iph->payload_len = old_iph->payload_len; be16_add_cpu(&iph->payload_len, sizeof(*old_iph)); iph->priority = old_iph->priority; memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl)); iph->daddr = cp->daddr.in6; iph->saddr = saddr; iph->hop_limit = old_iph->hop_limit; /* Another hack: avoid icmp_send in ip_fragment */ skb->local_df = 1; ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) ip6_local_out(skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: kfree_skb(skb); rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; }
int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { struct rt6_info *rt; /* Route to the other host */ struct in6_addr saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ __u8 next_protocol = 0; __u32 payload_len = 0; __u8 dsfield = 0; __u8 ttl = 0; struct ipv6hdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ int ret, local; EnterFunction(10); rcu_read_lock(); local = __ip_vs_get_out_rt_v6(cp->af, skb, cp->dest, &cp->daddr.in6, &saddr, ipvsh, 1, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_TUNNEL); if (local < 0) goto tx_error; if (local) { rcu_read_unlock(); return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); } rt = (struct rt6_info *) skb_dst(skb); tdev = rt->dst.dev; /* * Okay, now see if we can stuff it in the buffer as-is. */ max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr); skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom, &next_protocol, &payload_len, &dsfield, &ttl, NULL); if (IS_ERR(skb)) goto tx_error; skb = iptunnel_handle_offloads( skb, false, __tun_gso_type_mask(AF_INET6, cp->af)); if (IS_ERR(skb)) goto tx_error; skb->transport_header = skb->network_header; skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* * Push down and install the IPIP header. */ iph = ipv6_hdr(skb); iph->version = 6; iph->nexthdr = next_protocol; iph->payload_len = htons(payload_len); memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl)); ipv6_change_dsfield(iph, 0, dsfield); iph->daddr = cp->daddr.in6; iph->saddr = saddr; iph->hop_limit = ttl; /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) ip6_local_out(skb); else if (ret == NF_DROP) kfree_skb(skb); rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: if (!IS_ERR(skb)) kfree_skb(skb); rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; }
int ip6_push_pending_frames(struct sock *sk) { struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct net *net = sock_net(sk); struct ipv6hdr *hdr; struct ipv6_txoptions *opt = np->cork.opt; struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst; struct flowi6 *fl6 = &inet->cork.fl.u.ip6; unsigned char proto = fl6->flowi6_proto; int err = 0; if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL) goto out; tail_skb = &(skb_shinfo(skb)->frag_list); /* move skb->data to ip header from ext header */ if (skb->data < skb_network_header(skb)) __skb_pull(skb, skb_network_offset(skb)); while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); skb->len += tmp_skb->len; skb->data_len += tmp_skb->len; skb->truesize += tmp_skb->truesize; tmp_skb->destructor = NULL; tmp_skb->sk = NULL; } /* Allow local fragmentation. */ if (np->pmtudisc < IPV6_PMTUDISC_DO) skb->local_df = 1; ipv6_addr_copy(final_dst, &fl6->daddr); __skb_pull(skb, skb_network_header_len(skb)); if (opt && opt->opt_flen) ipv6_push_frag_opts(skb, opt, &proto); if (opt && opt->opt_nflen) ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst); skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); hdr = ipv6_hdr(skb); *(__be32*)hdr = fl6->flowlabel | htonl(0x60000000 | ((int)np->cork.tclass << 20)); hdr->hop_limit = np->cork.hop_limit; hdr->nexthdr = proto; ipv6_addr_copy(&hdr->saddr, &fl6->saddr); ipv6_addr_copy(&hdr->daddr, final_dst); skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; skb_dst_set(skb, dst_clone(&rt->dst)); IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); if (proto == IPPROTO_ICMPV6) { struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); ICMP6MSGOUT_INC_STATS_BH(net, idev, icmp6_hdr(skb)->icmp6_type); ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); } err = ip6_local_out(skb); if (err) { if (err > 0) err = net_xmit_errno(err); if (err) goto error; } out: ip6_cork_release(inet, np); return err; error: IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); goto out; }
static int ip6_tnl_xmit2(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, struct flowi *fl, int encap_limit, __u32 *pmtu) { struct net *net = dev_net(dev); struct ip6_tnl *t = netdev_priv(dev); struct net_device_stats *stats = &t->dev->stats; struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6_tel_txoption opt; struct dst_entry *dst; struct net_device *tdev; int mtu; unsigned int max_headroom = sizeof(struct ipv6hdr); u8 proto; int err = -1; int pkt_len; if ((dst = ip6_tnl_dst_check(t)) != NULL) dst_hold(dst); else { dst = ip6_route_output(net, NULL, fl); if (dst->error || xfrm_lookup(net, &dst, fl, NULL, 0) < 0) goto tx_err_link_failure; } tdev = dst->dev; if (tdev == dev) { stats->collisions++; if (net_ratelimit()) printk(KERN_WARNING "%s: Local routing loop detected!\n", t->parms.name); goto tx_err_dst_release; } mtu = dst_mtu(dst) - sizeof (*ipv6h); if (encap_limit >= 0) { max_headroom += 8; mtu -= 8; } if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; if (skb_dst(skb)) skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu); if (skb->len > mtu) { *pmtu = mtu; err = -EMSGSIZE; goto tx_err_dst_release; } /* * Okay, now see if we can stuff it in the buffer as-is. */ max_headroom += LL_RESERVED_SPACE(tdev); if (skb_headroom(skb) < max_headroom || skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { struct sk_buff *new_skb; if (!(new_skb = skb_realloc_headroom(skb, max_headroom))) goto tx_err_dst_release; if (skb->sk) skb_set_owner_w(new_skb, skb->sk); kfree_skb(skb); skb = new_skb; } skb_dst_drop(skb); skb_dst_set(skb, dst_clone(dst)); skb->transport_header = skb->network_header; proto = fl->proto; if (encap_limit >= 0) { init_tel_txopt(&opt, encap_limit); ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); } skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); ipv6h = ipv6_hdr(skb); *(__be32*)ipv6h = fl->fl6_flowlabel | htonl(0x60000000); dsfield = INET_ECN_encapsulate(0, dsfield); ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = proto; ipv6_addr_copy(&ipv6h->saddr, &fl->fl6_src); ipv6_addr_copy(&ipv6h->daddr, &fl->fl6_dst); nf_reset(skb); pkt_len = skb->len; err = ip6_local_out(skb); if (net_xmit_eval(err) == 0) { stats->tx_bytes += pkt_len; stats->tx_packets++; } else { stats->tx_errors++; stats->tx_aborted_errors++; } ip6_tnl_dst_store(t, dst); return 0; tx_err_link_failure: stats->tx_carrier_errors++; dst_link_failure(skb); tx_err_dst_release: dst_release(dst); return err; }
static void send_reset(struct net *net, struct sk_buff *oldskb) { struct sk_buff *nskb; struct tcphdr otcph, *tcph; unsigned int otcplen, hh_len; int tcphoff, needs_ack; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; #define DEFAULT_TOS_VALUE 0x0U const __u8 tclass = DEFAULT_TOS_VALUE; struct dst_entry *dst = NULL; u8 proto; __be16 frag_off; struct flowi6 fl6; if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { pr_debug("addr is not unicast.\n"); return; } proto = oip6h->nexthdr; tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); if ((tcphoff < 0) || (tcphoff > oldskb->len)) { pr_debug("Cannot get TCP header.\n"); return; } otcplen = oldskb->len - tcphoff; if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { pr_debug("proto(%d) != IPPROTO_TCP, " "or too short. otcplen = %d\n", proto, otcplen); return; } if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) BUG(); if (otcph.rst) { pr_debug("RST is set\n"); return; } if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, skb_checksum(oldskb, tcphoff, otcplen, 0))) { pr_debug("TCP checksum is invalid\n"); return; } memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; fl6.saddr = oip6h->daddr; fl6.daddr = oip6h->saddr; fl6.fl6_sport = otcph.dest; fl6.fl6_dport = otcph.source; security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); dst = ip6_route_output(net, NULL, &fl6); if (dst == NULL || dst->error) { dst_release(dst); return; } dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); if (IS_ERR(dst)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + dst->trailer_len, GFP_ATOMIC); if (!nskb) { net_dbg_ratelimited("cannot alloc skb\n"); dst_release(dst); return; } skb_dst_set(nskb, dst); skb_reserve(nskb, hh_len + dst->header_len); skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); ip6_flow_hdr(ip6h, tclass, 0); ip6h->hop_limit = ip6_dst_hoplimit(dst); ip6h->nexthdr = IPPROTO_TCP; ip6h->saddr = oip6h->daddr; ip6h->daddr = oip6h->saddr; skb_reset_transport_header(nskb); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); tcph->doff = sizeof(struct tcphdr)/4; tcph->source = otcph.dest; tcph->dest = otcph.source; if (otcph.ack) { needs_ack = 0; tcph->seq = otcph.ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + otcplen - (otcph.doff<<2)); tcph->seq = 0; } ((u_int8_t *)tcph)[13] = 0; tcph->rst = 1; tcph->ack = needs_ack; tcph->window = 0; tcph->urg_ptr = 0; tcph->check = 0; tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr), 0)); nf_ct_attach(nskb, oldskb); ip6_local_out(nskb); }
static unsigned int echo_tg6(struct sk_buff *oldskb, const struct xt_action_param *par) { const struct udphdr *oldudp; const struct ipv6hdr *oldip; struct udphdr *newudp, oldudp_buf; struct ipv6hdr *newip; struct sk_buff *newskb; unsigned int data_len; void *payload; struct flowi6 fl; struct dst_entry *dst = NULL; struct net *net = dev_net((par->in != NULL) ? par->in : par->out); /* This allows us to do the copy operation in fewer lines of code. */ if (skb_linearize(oldskb) < 0) return NF_DROP; oldip = ipv6_hdr(oldskb); oldudp = skb_header_pointer(oldskb, par->thoff, sizeof(*oldudp), &oldudp_buf); if (oldudp == NULL) return NF_DROP; if (ntohs(oldudp->len) <= sizeof(*oldudp)) return NF_DROP; newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + ntohs(oldudp->len), GFP_ATOMIC); if (newskb == NULL) return NF_DROP; skb_reserve(newskb, LL_MAX_HEADER); newskb->protocol = oldskb->protocol; skb_reset_network_header(newskb); newip = (void *)skb_put(newskb, sizeof(*newip)); newip->version = oldip->version; newip->priority = oldip->priority; memcpy(newip->flow_lbl, oldip->flow_lbl, sizeof(newip->flow_lbl)); newip->nexthdr = par->target->proto; newip->saddr = oldip->daddr; newip->daddr = oldip->saddr; skb_reset_transport_header(newskb); newudp = (void *)skb_put(newskb, sizeof(*newudp)); newudp->source = oldudp->dest; newudp->dest = oldudp->source; newudp->len = oldudp->len; data_len = htons(oldudp->len) - sizeof(*oldudp); payload = skb_header_pointer(oldskb, par->thoff + sizeof(*oldudp), data_len, NULL); memcpy(skb_put(newskb, data_len), payload, data_len); #if 0 /* * Since no fields are modified (we just swapped things around), * this works too in our specific echo case. */ newudp->check = oldudp->check; #else newudp->check = 0; newudp->check = csum_ipv6_magic(&newip->saddr, &newip->daddr, ntohs(newudp->len), IPPROTO_UDP, csum_partial(newudp, ntohs(newudp->len), 0)); #endif memset(&fl, 0, sizeof(fl)); fl.flowi6_proto = newip->nexthdr; memcpy(&fl.saddr, &newip->saddr, sizeof(fl.saddr)); memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr)); fl.fl6_sport = newudp->source; fl.fl6_dport = newudp->dest; security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl)); dst = ip6_route_output(net, NULL, &fl); if (dst == NULL || dst->error != 0) { dst_release(dst); goto free_nskb; } skb_dst_set(newskb, dst); newip->hop_limit = ip6_dst_hoplimit(skb_dst(newskb)); newskb->ip_summed = CHECKSUM_NONE; /* "Never happens" (?) */ if (newskb->len > dst_mtu(skb_dst(newskb))) goto free_nskb; nf_ct_attach(newskb, oldskb); ip6_local_out(newskb); return NF_DROP; free_nskb: kfree_skb(newskb); return NF_DROP; }
/* Send RST reply */ static void send_reset(struct net *net, struct sk_buff *oldskb) { struct sk_buff *nskb; struct tcphdr otcph, *tcph; unsigned int otcplen, hh_len; int tcphoff, needs_ack; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; #define DEFAULT_TOS_VALUE 0x0U const __u8 tclass = DEFAULT_TOS_VALUE; struct dst_entry *dst = NULL; u8 proto; struct flowi fl; if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { pr_debug("addr is not unicast.\n"); return; } proto = oip6h->nexthdr; tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto); if ((tcphoff < 0) || (tcphoff > oldskb->len)) { pr_debug("Cannot get TCP header.\n"); return; } otcplen = oldskb->len - tcphoff; /* IP header checks: fragment, too short. */ if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { pr_debug("proto(%d) != IPPROTO_TCP, " "or too short. otcplen = %d\n", proto, otcplen); return; } if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) BUG(); /* No RST for RST. */ if (otcph.rst) { pr_debug("RST is set\n"); return; } /* Check checksum. */ if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, skb_checksum(oldskb, tcphoff, otcplen, 0))) { pr_debug("TCP checksum is invalid\n"); return; } memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_TCP; ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr); ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr); fl.fl_ip_sport = otcph.dest; fl.fl_ip_dport = otcph.source; security_skb_classify_flow(oldskb, &fl); dst = ip6_route_output(net, NULL, &fl); if (dst == NULL || dst->error) { dst_release(dst); return; } if (xfrm_lookup(net, &dst, &fl, NULL, 0)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + dst->trailer_len, GFP_ATOMIC); if (!nskb) { if (net_ratelimit()) pr_debug("cannot alloc skb\n"); dst_release(dst); return; } skb_dst_set(nskb, dst); skb_reserve(nskb, hh_len + dst->header_len); skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); *(__be32 *)ip6h = htonl(0x60000000 | (tclass << 20)); ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); ip6h->nexthdr = IPPROTO_TCP; ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); /* Truncate to length (no data) */ tcph->doff = sizeof(struct tcphdr)/4; tcph->source = otcph.dest; tcph->dest = otcph.source; if (otcph.ack) { needs_ack = 0; tcph->seq = otcph.ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + otcplen - (otcph.doff<<2)); tcph->seq = 0; } /* Reset flags */ ((u_int8_t *)tcph)[13] = 0; tcph->rst = 1; tcph->ack = needs_ack; tcph->window = 0; tcph->urg_ptr = 0; tcph->check = 0; /* Adjust TCP checksum */ tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, &ipv6_hdr(nskb)->daddr, sizeof(struct tcphdr), IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr), 0)); nf_ct_attach(nskb, oldskb); ip6_local_out(nskb); }
static int output_ipv6(struct sk_buff *skb) { memset(IP6CB(skb), 0, sizeof (*IP6CB(skb))); #undef ip6_local_out return ip6_local_out(skb); }
int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp) { struct rt6_info *rt; /* Route to the other host */ struct in6_addr saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ struct ipv6hdr *old_iph = ipv6_hdr(skb); struct ipv6hdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ int mtu; int ret; EnterFunction(10); if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, &saddr, 1, 1|2))) goto tx_error_icmp; if (__ip_vs_is_local_route6(rt)) { dst_release(&rt->dst); IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 1); } tdev = rt->dst.dev; mtu = dst_mtu(&rt->dst) - sizeof(struct ipv6hdr); if (mtu < IPV6_MIN_MTU) { IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__, IPV6_MIN_MTU); goto tx_error_put; } if (skb_dst(skb)) skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu); if (mtu < ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr) && !skb_is_gso(skb)) { if (!skb->dev) { struct net *net = dev_net(skb_dst(skb)->dev); skb->dev = net->loopback_dev; } icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error_put; } /* * Okay, now see if we can stuff it in the buffer as-is. */ max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr); if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) { struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) { dst_release(&rt->dst); kfree_skb(skb); IP_VS_ERR_RL("%s(): no memory\n", __func__); return NF_STOLEN; } kfree_skb(skb); skb = new_skb; old_iph = ipv6_hdr(skb); } skb->transport_header = skb->network_header; skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* drop old route */ skb_dst_drop(skb); skb_dst_set(skb, &rt->dst); /* * Push down and install the IPIP header. */ iph = ipv6_hdr(skb); iph->version = 6; iph->nexthdr = IPPROTO_IPV6; iph->payload_len = old_iph->payload_len; be16_add_cpu(&iph->payload_len, sizeof(*old_iph)); iph->priority = old_iph->priority; memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl)); ipv6_addr_copy(&iph->daddr, &cp->daddr.in6); ipv6_addr_copy(&iph->saddr, &saddr); iph->hop_limit = old_iph->hop_limit; /* Another hack: avoid icmp_send in ip_fragment */ skb->local_df = 1; ret = IP_VS_XMIT_TUNNEL(skb, cp); if (ret == NF_ACCEPT) ip6_local_out(skb); else if (ret == NF_DROP) kfree_skb(skb); LeaveFunction(10); return NF_STOLEN; tx_error_icmp: dst_link_failure(skb); tx_error: kfree_skb(skb); LeaveFunction(10); return NF_STOLEN; tx_error_put: dst_release(&rt->dst); goto tx_error; }
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; struct tcphdr _otcph; const struct tcphdr *otcph; unsigned int otcplen, hh_len; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; struct dst_entry *dst = NULL; struct flowi6 fl6; if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { pr_debug("addr is not unicast.\n"); return; } otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook); if (!otcph) return; memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; fl6.saddr = oip6h->daddr; fl6.daddr = oip6h->saddr; fl6.fl6_sport = otcph->dest; fl6.fl6_dport = otcph->source; security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); dst = ip6_route_output(net, NULL, &fl6); if (dst == NULL || dst->error) { dst_release(dst); return; } dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); if (IS_ERR(dst)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + dst->trailer_len, GFP_ATOMIC); if (!nskb) { net_dbg_ratelimited("cannot alloc skb\n"); dst_release(dst); return; } skb_dst_set(nskb, dst); skb_reserve(nskb, hh_len + dst->header_len); ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, ip6_dst_hoplimit(dst)); nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen); nf_ct_attach(nskb, oldskb); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) /* If we use ip6_local_out for bridged traffic, the MAC source on * the RST will be ours, instead of the destination's. This confuses * some routers/firewalls, and they drop the packet. So we need to * build the eth header using the original destination's MAC as the * source, and send the RST packet directly. */ if (oldskb->nf_bridge) { struct ethhdr *oeth = eth_hdr(oldskb); nskb->dev = nf_bridge_get_physindev(oldskb); nskb->protocol = htons(ETH_P_IPV6); ip6h->payload_len = htons(sizeof(struct tcphdr)); if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), oeth->h_source, oeth->h_dest, nskb->len) < 0) return; dev_queue_xmit(nskb); } else #endif ip6_local_out(net, nskb->sk, nskb); }