static unsigned int mark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_mark_tginfo2 *info = par->targinfo; skb->mark = (skb->mark & ~info->mask) ^ info->mark; #ifdef HNDCTF { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if(ct) ct->ctf_flags |= CTF_FLAGS_EXCLUDED; } #endif /* HNDCTF */ return XT_CONTINUE; }
/* function to be called by hook */ static unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_conn_counter *acct; if (!skb) { return NF_ACCEPT; } if (in == NULL) { return NF_ACCEPT; } else if (in->name == NULL) { return NF_ACCEPT; } if (memcmp(devname, "all", 3) != 0) { if (memcmp(devname, in->name, 4) != 0) { return NF_ACCEPT; } } /* conntrack check */ if (skb->nfct == NULL) { /* if (net_ratelimit()) * pr_info("skb->nfct == NULL!\n"); */ return NF_ACCEPT; } ct = nf_ct_get(skb, &ctinfo); acct = nf_conn_acct_find(ct); if (!acct) { if (net_ratelimit()) pr_info("acct is NULL\n"); return NF_ACCEPT; } if (ct->proto.tcp.state == TCP_CONNTRACK_CLOSE_WAIT) { pr_info("%pI4 --> %pI4: %llu bytes CLOSE_WAIT\n", &iph->saddr, &iph->daddr, acct[IP_CT_DIR_ORIGINAL].bytes + acct[IP_CT_DIR_REPLY].bytes); } return NF_ACCEPT; }
static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, enum sdp_header_types term, char *buffer, int buflen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); unsigned int matchlen, matchoff; if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, &matchoff, &matchlen) <= 0) return -ENOENT; return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL; }
static unsigned int connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_connmark_tginfo1 *info = par->targinfo; enum ip_conntrack_info ctinfo; struct nf_conn *ct; u_int32_t newmark; ct = nf_ct_get(skb, &ctinfo); if (ct == NULL) return XT_CONTINUE; switch (info->mode) { case XT_CONNMARK_SET: newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; if (ct->mark != newmark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, ct); } break; case XT_CONNMARK_SAVE: newmark = (ct->mark & ~info->ctmask) ^ (skb->mark & info->nfmask); if (ct->mark != newmark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, ct); } // ------------- START of KNOX_VPN -----------------// knoxvpn_uidpid(skb,newmark); // ------------- END of KNOX_VPN -------------------// break; case XT_CONNMARK_RESTORE: newmark = (skb->mark & ~info->nfmask) ^ (ct->mark & info->ctmask); skb->mark = newmark; // ------------- START of KNOX_VPN -----------------// knoxvpn_uidpid(skb,newmark); // ------------- END of KNOX_VPN -------------------// break; } return XT_CONTINUE; }
static unsigned int ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_DST); }
/* 'skb' should already be pulled to nh_ofs. */ static int ovs_ct_helper(struct sk_buff *skb, u16 proto) { const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; enum ip_conntrack_info ctinfo; unsigned int protoff; struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED_REPLY) return NF_ACCEPT; help = nfct_help(ct); if (!help) return NF_ACCEPT; helper = rcu_dereference(help->helper); if (!helper) return NF_ACCEPT; switch (proto) { case NFPROTO_IPV4: protoff = ip_hdrlen(skb); break; case NFPROTO_IPV6: { u8 nexthdr = ipv6_hdr(skb)->nexthdr; __be16 frag_off; int ofs; ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, &frag_off); if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { pr_debug("proto header not found\n"); return NF_ACCEPT; } protoff = ofs; break; } default: WARN_ONCE(1, "helper invoked on non-IP family!"); return NF_DROP; } return helper->help(skb, protoff, ct, ctinfo); }
/* * vlantag_match_pre() * One of the iptables hooks has a packet for us to analyze, do so. */ static bool vlantag_match_pre(const struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_vlantag_match_info *info = par->targinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_ct_vlantag_ext *ncve; unsigned short int prio; if (!is_vlan_dev(skb->dev)) { DEBUGP("pre dev %s is not vlan\n", skb->dev->name); return false; } ct = nf_ct_get(skb, &ctinfo); if (!ct) { DEBUGP("pre xt_VLANTAG: No conntrack connection\n"); return false; } ncve = nf_ct_vlantag_ext_find(ct); if (!ncve) { DEBUGP("pre xt_VLANTAG: No conntrack extension\n"); return false; } if ((skb->priority < XT_VLANTAG_SKB_PRIO_MIN) || (skb->priority > XT_VLANTAG_SKB_PRIO_MAX)) { DEBUGP("pre dev: %s skb prioriy %d is out of range (%d - %d)", skb->dev->name, skb->priority, XT_VLANTAG_SKB_PRIO_MIN, XT_VLANTAG_SKB_PRIO_MAX); return false; } prio = skb->priority - XT_VLANTAG_SKB_PRIO_MIN; if ((prio & info->imask) == info->itag) { ncve->prio = prio; } else { ncve->prio = XT_VLANTAG_EXT_PRIO_NOT_VALID; } ncve->imask = info->imask; ncve->itag =info->itag; DEBUGP("xt_VLANTAG: pre match continue\n"); return true; }
static bool lscan_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_lscan_mtinfo *info = par->matchinfo; enum ip_conntrack_info ctstate; const struct tcphdr *tcph; struct nf_conn *ctdata; struct tcphdr tcph_buf; tcph = skb_header_pointer(skb, par->thoff, sizeof(tcph_buf), &tcph_buf); if (tcph == NULL) return false; /* Check for invalid packets: -m conntrack --ctstate INVALID */ if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) { if (info->match_stealth) return lscan_mt_stealth(tcph); /* * If @ctdata is NULL, we cannot match the other scan * types, return. */ return false; } /* * If -m lscan was previously applied to this packet, the rules we * simulate must not be run through again. And for speedup, do not call * it either when the connection is already VALID. */ if ((ctdata->mark & connmark_mask) == mark_valid || (skb_nfmark(skb) & packet_mask) != mark_seen) { unsigned int n; n = lscan_mt_full(ctdata->mark & connmark_mask, ctstate, par->state->in == init_net.loopback_dev, tcph, skb->len - par->thoff - 4 * tcph->doff); ctdata->mark = (ctdata->mark & ~connmark_mask) | n; skb_nfmark(skb) = (skb_nfmark(skb) & ~packet_mask) ^ mark_seen; } return (info->match_syn && ctdata->mark == mark_synscan) || (info->match_cn && ctdata->mark == mark_cnscan) || (info->match_gr && ctdata->mark == mark_grscan); }
/* Source NAT */ static unsigned int ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct nf_nat_multi_range_compat *mr = par->targinfo; NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); ct = nf_ct_get(skb, &ctinfo); /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); NF_CT_ASSERT(par->out != NULL); return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); }
static unsigned int nf_nat_adjust(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; ct = nf_ct_get(skb, &ctinfo); if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { DEBUGP("nf_nat_standalone: adjusting sequence number\n"); if (!nf_nat_seq_adjust(skb, ct, ctinfo)) return NF_DROP; } return NF_ACCEPT; }
static bool state_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_state_info *sinfo = par->matchinfo; enum ip_conntrack_info ctinfo; unsigned int statebit; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if (!ct) statebit = XT_STATE_INVALID; else { if (nf_ct_is_untracked(ct)) statebit = XT_STATE_UNTRACKED; else statebit = XT_STATE_BIT(ctinfo); } return (sinfo->statemask & statebit); }
static unsigned int ipv4_confirm(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; unsigned int ret; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) goto out; help = nfct_help(ct); if (!help) goto out; /* rcu_read_lock()ed by nf_hook_slow */ helper = rcu_dereference(help->helper); if (!helper) goto out; ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ct, ctinfo); if (ret != NF_ACCEPT) return ret; if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { typeof(nf_nat_seq_adjust_hook) seq_adjust; seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); return NF_DROP; } } out: /* We've seen it coming out the other side: confirm it */ return nf_conntrack_confirm(skb); }
/* * vlantag_target_post() * One of the iptables hooks has a packet for us to analyze, do so. */ static bool vlantag_target_post(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_vlantag_target_info *info = par->targinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_ct_vlantag_ext *ncve; unsigned int vlan_prio; if (!is_vlan_dev(skb->dev)) { DEBUGP("post dev %s is not vlan\n", skb->dev->name); return true; } ct = nf_ct_get(skb, &ctinfo); if (!ct) { DEBUGP("post xt_VLANTAG: No conntrack connection\n"); return true; } ncve = nf_ct_vlantag_ext_find(ct); if (!ncve) { DEBUGP("post No vlan tag extension\n"); return true; } if (ncve->prio == XT_VLANTAG_EXT_PRIO_NOT_VALID) { DEBUGP("post vlan tag ext prio is not set\n"); if ((skb->priority >= XT_VLANTAG_SKB_PRIO_MIN) && (skb->priority <= XT_VLANTAG_SKB_PRIO_MAX)) { DEBUGP("but skb prio %d is in the range of our iptables rule, drop the packet\n", skb->priority); return false; } return true; } vlan_prio = (ncve->prio & info->omask) | info->oval; skb->priority = vlan_prio + XT_VLANTAG_SKB_PRIO_MIN; ncve->magic = true; ncve->omask = info->omask; ncve->oval = info->oval; DEBUGP("post xt_VLANTAG: post target continue\n"); return true; }
unsigned int tse6_adjust(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; ct = nf_ct_get(*pskb, &ctinfo); if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { if (!tse6_seq_adjust(pskb, ct, ctinfo)) { return NF_DROP; } } return NF_ACCEPT; }
/* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has * previously sent the packet to conntrack via the ct action. */ static void ovs_ct_update_key(const struct sk_buff *skb, struct sw_flow_key *key, bool post_ct) { const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; enum ip_conntrack_info ctinfo; struct nf_conn *ct; u8 state = 0; ct = nf_ct_get(skb, &ctinfo); if (ct) { state = ovs_ct_get_state(ctinfo); if (ct->master) state |= OVS_CS_F_RELATED; zone = nf_ct_zone(ct); } else if (post_ct) { state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID; } __ovs_ct_update_key(key, state, zone, ct); }
static bool state_mt(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { const struct xt_state_info *sinfo = matchinfo; enum ip_conntrack_info ctinfo; unsigned int statebit; if (nf_ct_is_untracked(skb)) statebit = XT_STATE_UNTRACKED; else if (!nf_ct_get(skb, &ctinfo)) statebit = XT_STATE_INVALID; else statebit = XT_STATE_BIT(ctinfo); return (sinfo->statemask & statebit); }
static bool xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct sk_buff *pskb = (struct sk_buff *)skb; const struct xt_cluster_match_info *info = par->matchinfo; const struct nf_conn *ct; enum ip_conntrack_info ctinfo; unsigned long hash; /* This match assumes that all nodes see the same packets. This can be * achieved if the switch that connects the cluster nodes support some * sort of 'port mirroring'. However, if your switch does not support * this, your cluster nodes can reply ARP request using a multicast MAC * address. Thus, your switch will flood the same packets to the * cluster nodes with the same multicast MAC address. Using a multicast * link address is a RFC 1812 (section 3.3.2) violation, but this works * fine in practise. * * Unfortunately, if you use the multicast MAC address, the link layer * sets skbuff's pkt_type to PACKET_MULTICAST, which is not accepted * by TCP and others for packets coming to this node. For that reason, * this match mangles skbuff's pkt_type if it detects a packet * addressed to a unicast address but using PACKET_MULTICAST. Yes, I * know, matches should not alter packets, but we are doing this here * because we would need to add a PKTTYPE target for this sole purpose. */ if (!xt_cluster_is_multicast_addr(skb, xt_family(par)) && skb->pkt_type == PACKET_MULTICAST) { pskb->pkt_type = PACKET_HOST; } ct = nf_ct_get(skb, &ctinfo); if (ct == NULL) return false; if (ct->master) hash = xt_cluster_hash(ct->master, info); else hash = xt_cluster_hash(ct, info); return !!((1 << hash) & info->node_mask) ^ !!(info->flags & XT_CLUSTER_F_INV); }
static unsigned int target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) { const struct xt_connmark_target_info *markinfo = targinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; u_int32_t diff; u_int32_t mark; u_int32_t newmark; ct = nf_ct_get(*pskb, &ctinfo); if (ct) { switch(markinfo->mode) { case XT_CONNMARK_SET: newmark = (ct->mark & ~markinfo->mask) | markinfo->mark; if (newmark != ct->mark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); } break; case XT_CONNMARK_SAVE: newmark = (ct->mark & ~markinfo->mask) | ((*pskb)->mark & markinfo->mask); if (ct->mark != newmark) { ct->mark = newmark; nf_conntrack_event_cache(IPCT_MARK, *pskb); } break; case XT_CONNMARK_RESTORE: mark = (*pskb)->mark; diff = (ct->mark ^ mark) & markinfo->mask; (*pskb)->mark = mark ^ diff; break; } } return XT_CONTINUE; }
unsigned int nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, struct nf_conn *ct)) { const struct nf_conn *ct; enum ip_conntrack_info ctinfo; unsigned int ret; int err; /* root is playing with raw sockets. */ if (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); if (ct->tuplehash[dir].tuple.dst.u3.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { err = ip_route_me_harder(skb, RTN_UNSPEC); if (err < 0) ret = NF_DROP_ERR(err); } #ifdef CONFIG_XFRM else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && ct->tuplehash[dir].tuple.dst.u.all != ct->tuplehash[!dir].tuple.src.u.all) { err = nf_xfrm_me_harder(skb, AF_INET); if (err < 0) ret = NF_DROP_ERR(err); } #endif } return ret; }
static unsigned int ipv6_confirm(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; struct nf_conn_help *help; struct nf_conntrack_helper *helper; enum ip_conntrack_info ctinfo; unsigned int ret, protoff; unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data; unsigned char pnum = ipv6_hdr(*pskb)->nexthdr; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(*pskb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) goto out; help = nfct_help(ct); if (!help) goto out; /* rcu_read_lock()ed by nf_hook_slow */ helper = rcu_dereference(help->helper); if (!helper) goto out; protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, (*pskb)->len - extoff); if (protoff > (*pskb)->len || pnum == NEXTHDR_FRAGMENT) { DEBUGP("proto header not found\n"); return NF_ACCEPT; } ret = helper->help(pskb, protoff, ct, ctinfo); if (ret != NF_ACCEPT) return ret; out: /* We've seen it coming out the other side: confirm it */ return nf_conntrack_confirm(pskb); }
static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) { struct flowi4 *fl4 = &fl->u.ip4; const struct nf_conn *ct; const struct nf_conntrack_tuple *t; enum ip_conntrack_info ctinfo; enum ip_conntrack_dir dir; unsigned long statusbit; ct = nf_ct_get(skb, &ctinfo); if (ct == NULL) return; dir = CTINFO2DIR(ctinfo); t = &ct->tuplehash[dir].tuple; if (dir == IP_CT_DIR_ORIGINAL) statusbit = IPS_DST_NAT; else statusbit = IPS_SRC_NAT; if (ct->status & statusbit) { fl4->daddr = t->dst.u3.ip; if (t->dst.protonum == IPPROTO_TCP || t->dst.protonum == IPPROTO_UDP || t->dst.protonum == IPPROTO_UDPLITE || t->dst.protonum == IPPROTO_DCCP || t->dst.protonum == IPPROTO_SCTP) fl4->fl4_dport = t->dst.u.tcp.port; } statusbit ^= IPS_NAT_MASK; if (ct->status & statusbit) { fl4->saddr = t->src.u3.ip; if (t->dst.protonum == IPPROTO_TCP || t->dst.protonum == IPPROTO_UDP || t->dst.protonum == IPPROTO_UDPLITE || t->dst.protonum == IPPROTO_DCCP || t->dst.protonum == IPPROTO_SCTP) fl4->fl4_sport = t->src.u.tcp.port; } }
static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, enum sdp_header_types term, const union nf_inet_addr *addr) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); char buffer[INET6_ADDRSTRLEN]; unsigned int buflen; buflen = sip_sprintf_addr(ct, buffer, addr, false); if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, type, term, buffer, buflen)) return 0; return mangle_content_len(skb, protoff, dataoff, dptr, datalen); }
static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, int *hotdrop) { const struct xt_connmark_info *info = matchinfo; struct nf_conn *ct; enum ip_conntrack_info ctinfo; ct = nf_ct_get(skb, &ctinfo); if (!ct) return 0; return (((ct->mark) & info->mask) == info->mark) ^ info->invert; }
static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum, struct sk_buff *skb) { u16 zone_id = NF_CT_DEFAULT_ZONE_ID; #if IS_ENABLED(CONFIG_NF_CONNTRACK) if (skb->nfct) { enum ip_conntrack_info ctinfo; const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo)); } #endif if (nf_bridge_in_prerouting(skb)) return IP_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id; if (hooknum == NF_INET_PRE_ROUTING) return IP_DEFRAG_CONNTRACK_IN + zone_id; else return IP_DEFRAG_CONNTRACK_OUT + zone_id; }
unsigned int nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb, const struct nf_hook_state *state, unsigned int (*do_chain)(void *priv, struct sk_buff *skb, const struct nf_hook_state *state, struct nf_conn *ct)) { const struct nf_conn *ct; enum ip_conntrack_info ctinfo; unsigned int ret; int err; /* root is playing with raw sockets. */ if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; ret = nf_nat_ipv6_fn(priv, skb, state, do_chain); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, &ct->tuplehash[!dir].tuple.src.u3)) { err = ip6_route_me_harder(state->net, skb); if (err < 0) ret = NF_DROP_ERR(err); } #ifdef CONFIG_XFRM else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 && ct->tuplehash[dir].tuple.dst.u.all != ct->tuplehash[!dir].tuple.src.u.all) { err = nf_xfrm_me_harder(state->net, skb, AF_INET6); if (err < 0) ret = NF_DROP_ERR(err); } #endif } return ret; }
static void nft_nat_eval(const struct nft_expr *expr, struct nft_data data[NFT_REG_MAX + 1], const struct nft_pktinfo *pkt) { const struct nft_nat *priv = nft_expr_priv(expr); enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); struct nf_nat_range range; memset(&range, 0, sizeof(range)); if (priv->sreg_addr_min) { if (priv->family == AF_INET) { range.min_addr.ip = (__force __be32) data[priv->sreg_addr_min].data[0]; range.max_addr.ip = (__force __be32) data[priv->sreg_addr_max].data[0]; } else { memcpy(range.min_addr.ip6, data[priv->sreg_addr_min].data, sizeof(struct nft_data)); memcpy(range.max_addr.ip6, data[priv->sreg_addr_max].data, sizeof(struct nft_data)); } range.flags |= NF_NAT_RANGE_MAP_IPS; } if (priv->sreg_proto_min) { range.min_proto.all = *(__be16 *)&data[priv->sreg_proto_min].data[0]; range.max_proto.all = *(__be16 *)&data[priv->sreg_proto_max].data[0]; range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; } range.flags |= priv->flags; data[NFT_REG_VERDICT].verdict = nf_nat_setup_info(ct, &range, priv->type); }
static unsigned int ipv4_conntrack_local(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { if (ip_is_fragment(ip_hdr(skb))) { /* IP_NODEFRAG setsockopt set */ enum ip_conntrack_info ctinfo; struct nf_conn *tmpl; tmpl = nf_ct_get(skb, &ctinfo); if (tmpl && nf_ct_is_template(tmpl)) { /* when skipping ct, clear templates to avoid fooling * later targets/matches */ skb->_nfct = 0; nf_ct_put(tmpl); } return NF_ACCEPT; } return nf_conntrack_in(state->net, PF_INET, state->hook, skb); }
/* 获取缓存socket的HOOK函数 */ static unsigned int ipv4_conntrack_restore_sock (unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_conntrack_ext *exts; ct = nf_ct_get(skb, &ctinfo); if (!ct || ct == &nf_conntrack_untracked){ goto out; } if ((ip_hdr(skb)->protocol != IPPROTO_UDP) && (ip_hdr(skb)->protocol != IPPROTO_TCP)) { goto out; } exts = nf_conn_exts_find(ct); if (exts) { /* 获取缓存的socket */ if (exts->bits_idx[CONN_SOCK] != -1) { struct sock *sk = (struct sock *)nf_ct_exts_get(ct, exts->bits_idx[CONN_SOCK]); if (sk) { if ((ip_hdr(skb)->protocol == IPPROTO_TCP) && sk->sk_state != TCP_ESTABLISHED) { goto out; } if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { goto out; } skb_orphan(skb); skb->sk = sk; /* 曾经在上面atomic inc了引用计数,等到转交给下任owner的时候,一定要put */ skb->destructor = nf_ext_destructor; } } } out: return NF_ACCEPT; }
unsigned int nf_nat_ipv4_out(void *priv, struct sk_buff *skb, const struct nf_hook_state *state, unsigned int (*do_chain)(void *priv, struct sk_buff *skb, const struct nf_hook_state *state, struct nf_conn *ct)) { #ifdef CONFIG_XFRM const struct nf_conn *ct; enum ip_conntrack_info ctinfo; int err; #endif unsigned int ret; /* root is playing with raw sockets. */ if (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; ret = nf_nat_ipv4_fn(priv, skb, state, do_chain); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); if ((ct->tuplehash[dir].tuple.src.u3.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) || (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && ct->tuplehash[dir].tuple.src.u.all != ct->tuplehash[!dir].tuple.dst.u.all)) { err = nf_xfrm_me_harder(state->net, skb, AF_INET); if (err < 0) ret = NF_DROP_ERR(err); } } #endif return ret; }
unsigned int nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int (*do_chain)(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, struct nf_conn *ct)) { #ifdef CONFIG_XFRM const struct nf_conn *ct; enum ip_conntrack_info ctinfo; int err; #endif unsigned int ret; /* root is playing with raw sockets. */ if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3) || (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 && ct->tuplehash[dir].tuple.src.u.all != ct->tuplehash[!dir].tuple.dst.u.all)) { err = nf_xfrm_me_harder(skb, AF_INET6); if (err < 0) ret = NF_DROP_ERR(err); } } #endif return ret; }