netfilter和iptables--netfilter源码篇

news2024/9/24 11:30:57

netfilter和iptables–netfilter源码篇

防火墙是保护服务器和基础设施的重要工具,在Linux系统下,目前广泛使用的防火墙工具是iptables,但实际进行规则实施并产生实际作用的是Netfilter,iptables与内核中的netfilter框架中Hook协同工作,实现IP数据包的控制与过滤,本次将基于Linux6.5从源码视角分析。

总视角下网络通信协议栈模式如下:

在这里插入图片描述
图中各个HOOK点触发情况:

1)PREROUTING:NF_IP_PRE_ROUTING 
(2)INPUT:NF_IP_LOCAL_IN 
(3)FORWARD:NF_IP_FORWARD 
(4)OUTPUT:NF_IP_LOCAL_OUT 
(5)POSTROUTING:NF_IP_POST_ROUTING 

显而易见,数据包在netfiler框架中的流向取决于路由判定,第一次路由判定是通过检查输入的数据包的IP头部,根据目的地址判定请求的是否是本地主机IP地址,如果请求的是本机IP,数据包发给本机,否则说明请求的IP并非本机,进行路由转发;第二次次路由判定是根据输出的数据包IP头部,根据路由表中路由信息获取下一跳主机/网关IP,进行转发,其数据包流向路径分为三部分。

1、发往本地

数据包经过Hook点情况:NF_INET_PRE_ROUTING–>NF_INET_LOCAL_IN,当数据包从网卡进入Linux网络协议栈首先触发NF_INET_PRE_ROUTING,经过路由判决,请求目的IP为本机,触发NF_INET_LOCAL_IN

2、转发数据

数据包经过Hook点情况:NF_INET_PRE_ROUTING–>NF_INET_FORWARD–>NF_INET_POST_ROUTING,接收到的数据包经过路由判决,发现请求的目的IP为其他机器,触发NF_INET_FORWARD,完成处理后触发NF_INET_POST_ROUTING

3、本地发出

数据包经过Hook点情况:NF_INET_LOCAL_OUT–>NF_INET_POST_ROUTING,本机产生的数据包进入Linux协议栈立即触发NF_INET_LOCAL_OUT,对于三种路径的数据包最终均会触发NF_INET_POST_ROUTING

源码视角下函数调用源码分析:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(1)发往本地
ip_rcv()
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,struct net_device *orig_dev)
{
  struct net *net = dev_net(dev);
  skb = ip_rcv_core(skb, net);
  if (skb == NULL)return NET_RX_DROP;
  /*
  1、调用 Netfilter 钩子 NF_INET_PRE_ROUTING,对数据包进行预路由阶段处理
  2、skb 传递给 ip_rcv_finish 
  */
  return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
​        net, NULL, skb, dev, NULL,
​        ip_rcv_finish);
}
ip_rcv_finish()
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
  // 获取当前数据包关联的网络设备
  struct net_device *dev = skb->dev;
  int ret;
  /* if ingress device is enslaved to an L3 master device pass the
   \* skb to its handler for processing
   */
  skb = l3mdev_ip_rcv(skb);
  if (!skb)return NET_RX_SUCCESS;
  // 调用 ip_rcv_finish_core 进行进一步的核心 IP 处理
  ret = ip_rcv_finish_core(net, sk, skb, dev, NULL);
 // 如果数据包未被丢弃,则继续调用 dst_input 处理路由和传递
  if (ret != NET_RX_DROP)
​    ret = dst_input(skb);
  return ret;
}
dst_input()
static inline int dst_input(struct sk_buff *skb)
{
    // 调用 skb 中 dst_entry 结构体的 input 函数指针,使用 INDIRECT_CALL_INET 进行间接调用
    // 如果是 IPv6 数据包,则调用 ip6_input
    // 如果是 IPv4 数据包,则调用 ip_local_deliver
    return INDIRECT_CALL_INET(skb_dst(skb)->input,
                              ip6_input, ip_local_deliver, skb);
}
ip_local_deliver()
int ip_local_deliver(struct sk_buff *skb)
{
  /*
   \*  Reassemble IP fragments.
   */
  struct net *net = dev_net(skb->dev);
  // 检查数据包是否分片
  if (ip_is_fragment(ip_hdr(skb))) {//分片调用ip_defrag重组if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))return 0;
  }
  /*
  1、调用 Netfilter 的 NF_INET_LOCAL_IN 钩子,允许 Netfilter 在本地传输前进行处理
  2、成功后调用 ip_local_deliver_finish 处理
  */
  return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
​        net, NULL, skb, skb->dev, NULL,
​        ip_local_deliver_finish);
}

数据包在发往本地路径上,当执行iptables的input规则时,经过PREROUTING 链和INPUT 链,触发NF_INET_PRE_ROUTING,用于目的地址转换/数据包修改(nat表和mangle表);触发NF_INET_LOCAL_INHook点,控制数据包是否可以传递到本地协议栈。

(2)转发数据
ip_route_input_noref()

ip_rcv_finish()函数中调用ip_rcv_finish_core()进行核心IP处理,进行路由提示检查、根据IP协议选择TCP解复用以及UDP解复用、检查skb是否是有效的路由缓存,没有缓存进入ip_route_input_noref进入路由查找,存在路由缓存则从设备dev获取in_device结构,设置IPSKB_NOPOLICY

int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,

​       u8 tos, struct net_device *dev)
{
  struct fib_result res;
  int err;
  //屏蔽掉 tos 字段中不需要的位,通常只保留服务类型的高两位,表示优先级。
  tos &= IPTOS_RT_MASK;
  rcu_read_lock();
  //RCU锁机制保护下进行实际的路由查找
  err = ip_route_input_rcu(skb, daddr, saddr, tos, dev, &res);
  rcu_read_unlock();
  return err;
}
EXPORT_SYMBOL(ip_route_input_noref);
ip_route_input_rcu()

该函数中进行实际的路由查找,将路由查找的结果存储在fib_result

  • /* called with rcu_read_lock held */
    static int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                      u8 tos, struct net_device *dev, struct fib_result *res)
    {
            //目的ip是多播地址
             if (ipv4_is_multicast(daddr)) {
              //获取数据包接收设备的in_device结构
              struct in_device *in_dev = __in_dev_get_rcu(dev);
              int our = 0;
              int err = -EINVAL;
    
         if (!in_dev)
             return err;
         //检查目标多播地址和源地址是否与设备 in_dev 上配置的多播组匹配。返回值布尔值 our,表示该设备是否参与了该多播组。
         our = ip_check_mc_rcu(in_dev, daddr, saddr,
                       ip_hdr(skb)->protocol);
    
         /* check l3 master if no match yet */
         if (!our && netif_is_l3_slave(dev)) {
             struct in_device *l3_in_dev;
             l3_in_dev = __in_dev_get_rcu(skb->dev);
             if (l3_in_dev)
                 our = ip_check_mc_rcu(l3_in_dev, daddr, saddr,
                               ip_hdr(skb)->protocol);
         }
         //多播路由输入处理
         //匹配成功且启用IP多播路由转发CONFIG_IP_MROUTE,设备配置为多播转发器IN_DEV_MFORWARD进行多播路由处理
         if (our
         #ifdef CONFIG_IP_MROUTE
             ||
             (!ipv4_is_local_multicast(daddr) &&
              IN_DEV_MFORWARD(in_dev))
         #endif
            ) {
             //多播IP路由查找
             err = ip_route_input_mc(skb, daddr, saddr,
                         tos, dev, our);
         }
         return err;
             }
             //daddr是非多播地址/不匹配,数据包回退置慢路径,进入常规路由查找
             return ip_route_input_slow(skb, daddr, saddr, tos, dev, res);
         }
    
ip_route_input_slow()

处理网络数据包的慢速路径路由查找,特别是在路由缓存不可用或者需要更复杂处理时使用。

static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
​          u8 tos, struct net_device *dev,struct fib_result *res)
{
  struct in_device *in_dev = __in_dev_get_rcu(dev);
  struct flow_keys *flkeys = NULL, _flkeys;
  struct net   *net = dev_net(dev);
  struct ip_tunnel_info *tun_info;
  int   err = -EINVAL;
  unsigned int   flags = 0;
  u32   itag = 0;
  struct rtable  *rth;
  struct flowi4  fl4;
  bool do_cache = true;
  /* IP on this device is disabled. */
  if (!in_dev)goto out;
  /* Check for the most weird martians, which can be not detected
   \* by fib_lookup.
   */
  //tun_info获取数据包隧道信息,数据包如果通过隧道技术传输且传输模式不是TX,获取tun_id标识隧道连接
  tun_info = skb_tunnel_info(skb);
  if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
​    fl4.flowi4_tun_key.tun_id = tun_info->key.tun_id;
  else
​    fl4.flowi4_tun_key.tun_id = 0;
  //删除数据包路由缓存目标,每次进入慢速路径时,进行清除
  skb_dst_drop(skb);
  //检查saddr是否是多播地址/链路层广播地址
  if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))goto martian_source;
  res->fi = NULL;
  res->table = NULL;
  //daadr
  if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0))goto brd_input;
  /* Accept zero addresses only to limited broadcast;
   \* I even do not know to fix it or not. Waiting for complains :-)
   */
  if (ipv4_is_zeronet(saddr))goto martian_source;
  if (ipv4_is_zeronet(daddr))goto martian_destination;
  /* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(),
   \* and call it once if daddr or/and saddr are loopback addresses
   */
  if (ipv4_is_loopback(daddr)) {if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))goto martian_destination;
  } else if (ipv4_is_loopback(saddr)) {if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))goto martian_source;
      ......
  }
ip_mkroute_input()

处理多路径路由选择和调用低层函数创建路由缓存条目。内核配置中启用CONFIG_IP_ROUTE_MULTIPATH进行多路径路由选择,根据路由的查找结果fib_result中的fib_info是否存在,fib_multipath_hash检查路由条目是否大于1,选择进入多路径路由选择处理,fib_select_multipath根据哈希值选择合适的路径,并将选定的路径更新到 fib_result 中。其中哈希值的计算使用网络数据包的一些特征(如源地址、目的地址、端口号等确保这样相同流量会选择相同的路径,以保持数据包的顺序性

static int ip_mkroute_input(struct sk_buff *skb,struct fib_result *res,struct in_device *in_dev,
​        __be32 daddr, __be32 saddr, u32 tos,struct flow_keys *hkeys)
{
\#ifdef CONFIG_IP_ROUTE_MULTIPATH
  if (res->fi && fib_info_num_path(res->fi) > 1) {int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys);fib_select_multipath(res, h);
  }
\#endif
  /* create a routing cache entry */
  return __mkroute_input(skb, res, in_dev, daddr, saddr, tos);
}
__mkroute_input()

创建路由缓存条目,设置路由转发的处理函数。

/* called in rcu_read_lock() section */
static int __mkroute_input(struct sk_buff *skb,
               const struct fib_result *res,
               struct in_device *in_dev,
               __be32 daddr, __be32 saddr, u32 tos)
{
    struct fib_nh_common *nhc = FIB_RES_NHC(*res);
    struct net_device *dev = nhc->nhc_dev;
    struct fib_nh_exception *fnhe;
    struct rtable *rth;
    int err;
    struct in_device *out_dev;
    bool do_cache;
    u32 itag = 0;/* get a working reference to the output device */
​    out_dev = __in_dev_get_rcu(dev);if (!out_dev) {net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");return -EINVAL;}//地址是否有效
​    err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res),
​                  in_dev->dev, in_dev, &itag);if (err < 0) {ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
​                     saddr);goto cleanup;}//处理重定向和代理ARP//do_cache标识是否可以缓存路由,存在有效路由信息res->fi并且未标记itag
​    do_cache = res->fi && !itag;//如果输出设备是输入设备且err且设备允许重定向且数据包时IP类型,设置标志位IPSKB_DOREDIRECTif (out_dev == in_dev && err && IN_DEV_TX_REDIRECTS(out_dev) &&
​        skb->protocol == htons(ETH_P_IP)) {
​        __be32 gw;

​        gw = nhc->nhc_gw_family == AF_INET ? nhc->nhc_gw.ipv4 : 0;if (IN_DEV_SHARED_MEDIA(out_dev) ||inet_addr_onlink(out_dev, saddr, gw))IPCB(skb)->flags |= IPSKB_DOREDIRECT;}//不是IP类型在设备是在l3从属设备时检查代理ARP相关配置if (skb->protocol != htons(ETH_P_IP)) {/* Not IP (i.e. ARP). Do not create route, if it is

   * invalid for proxy arp. DNAT routes are always valid.

        * Proxy arp feature have been extended to allow, ARP
          lies back to the same interface, to support

             * Private VLAN switch technologies. See arp.c.

               ​        if (out_dev == in_dev &&
               IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
               err = -EINVAL;
               goto cleanup;
               ​        }
               ​    }
               ​    //检查设备是否配置了NOPOLICY,是则在数据包控制块中设置IPSKB_NOPOLICY,数据包不受IPsec/其他安全限制
               ​    if (IN_DEV_ORCONF(in_dev, NOPOLICY))
               ​        IPCB(skb)->flags |= IPSKB_NOPOLICY;
               ​    //查找异常路由
               ​    fnhe = find_exception(nhc, daddr);
               ​    if (do_cache) {
               ​        if (fnhe)
               rth = rcu_dereference(fnhe->fnhe_rth_input);
               ​        else
               rth = rcu_dereference(nhc->nhc_rth_input);
               ​        if (rt_cache_valid(rth)) {
               skb_dst_set_noref(skb, &rth->dst);
               goto out;
               ​        }
               ​    }
               ​    //分配新的路由缓存
               ​    rth = rt_dst_alloc(out_dev->dev, 0, res->type,
                  IN_DEV_ORCONF(out_dev, NOXFRM));
               ​    if (!rth) {
               ​        err = -ENOBUFS;
               ​        goto cleanup;
               ​    }
               ​    //设置路由属性
               ​    rth->rt_is_input = 1;//输入路由
               ​    RT_CACHE_STAT_INC(in_slow_tot);//更新路由缓存统计信息

​    rth->dst.input = ip_forward;//路由处理函数进行路由转发

​    rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag,
​               do_cache);//设置下一跳相关信息
​    lwtunnel_set_redirect(&rth->dst);//设置与隧道相关的重定向信息
​    skb_dst_set(skb, &rth->dst);//将数据包的目标设置为新分配的路由缓存
out:
​    err = 0;
 cleanup:
​    return err;
}
ip_forward()

主要用于处理 IPv4 数据包的转发逻辑,包括 TTL 处理、路径选择、MTU 检查、ICMP 响应等。

int ip_forward(struct sk_buff *skb)
{
    u32 mtu;
    struct iphdr *iph;  /* Our header */
    struct rtable *rt;  /* Route we use */
    struct ip_options *opt  = &(IPCB(skb)->opt);
    struct net *net;
    SKB_DR(reason);/* that should never happen *///主机发送的数据包丢弃if (skb->pkt_type != PACKET_HOST)goto drop;if (unlikely(skb->sk))goto drop;if (skb_warn_if_lro(skb))goto drop;if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) {SKB_DR_SET(reason, XFRM_POLICY);goto drop;}if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))return NET_RX_SUCCESS;//计算数据包校验和skb_forward_csum(skb);
​    net = dev_net(skb->dev);/*

   *  According to the RFC, we must first decrease the TTL field. If
        that reaches zero, we must reply an ICMP control message telling
         *  that the packet's lifetime expired.
            /
                //获取ttl并检查生存时间是否已过
                if (ip_hdr(skb)->ttl <= 1)
            goto too_many_hops;
                //数据包是否可以被转发
                if (!xfrm4_route_forward(skb)) {
            SKB_DR_SET(reason, XFRM_POLICY);
            goto drop;
                }
                //通过skb获取路由表
                rt = skb_rtable(skb);

​    if (opt->is_strictroute && rt->rt_uses_gateway)
​        goto sr_failed;
​    //标记已转发
​    IPCB(skb)->flags |= IPSKB_FORWARDED;
​    //获取目的地的最大传输单元(MTU)
​    mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
​    //检查数据包是否超过MTU
​    if (ip_exceeds_mtu(skb, mtu)) {
​        IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
​        //超过发送ICMP不可达消息,设置类型ICMP_FRAG_NEEDED
​        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
​              htonl(mtu));
​        SKB_DR_SET(reason, PKT_TOO_BIG);
​        goto drop;
​    }

​    /* We are about to mangle packet. Copy it! */if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))goto drop;
​    iph = ip_hdr(skb);/* Decrease ttl after skb cow done */ip_decrease_ttl(iph);/*

   *  We now generate an ICMP HOST REDIRECT giving the route
        we calculated.
           */
          if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
      !skb_sec_path(skb))
      ip_rt_send_redirect(skb);if (READ_ONCE(net->ipv4.sysctl_ip_fwd_update_priority))
​        skb->priority = rt_tos2priority(iph->tos);//使用 NF_HOOK 调用 IPv4 的转发钩子,执行数据包的转发逻辑,最终交给 ip_forward_finish 函数处理return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
​               net, NULL, skb, skb->dev, rt->dst.dev,
​               ip_forward_finish);
    ......
}
ip_forward_finish()

ipv4数据包转发

static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
  struct ip_options *opt  = &(IPCB(skb)->opt);
  //更新转发数据包统计,IPSTATS_MIB_OUTFORWDATAGRAMS成功转发的数据包数量
  __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
  //将当前数据包长度skb->len添加到总输出字节统计中,IPSTATS_MIB_OUTOCTETS成功转发的数据包总字节数
  __IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
\#ifdef CONFIG_NET_SWITCHDEV
  if (skb->offload_l3_fwd_mark) {consume_skb(skb);return 0;
  }
\#endif
  if (unlikely(opt->optlen))ip_forward_options(skb);
  skb_clear_tstamp(skb);
  //数据包发送到路由表中指定的下一跳,最终将数据包交付给相应的网络设备
  return dst_output(net, sk, skb);
}
dst_output()
static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	return INDIRECT_CALL_INET(skb_dst(skb)->output,
				  ip6_output, ip_output,
				  net, sk, skb);
}
(3)本地发出
ip_output()

处理 IPv4 数据包的发送,将数据包从网络协议栈传递到下层传输层。

int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev;
	//IPSTATS_MIB_OUT成功发送的数据包总字节数,IP_UPD_PO_STATS更新IPv4输出统计信息
	IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);

	skb->dev = dev;
	skb->protocol = htons(ETH_P_IP);
	//调用 Netfilter 钩子NF_INET_POST_ROUTING,处理数据包的转发过程,调用ip_finish_output完成数据包的发送
	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
			    net, sk, skb, indev, dev,
			    ip_finish_output,
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
}
EXPORT_SYMBOL(ip_output);
ip_finish_output()

发送 IPv4 数据包之前,结合 BPF 和 cgroup 机制对数据包进行处理,并根据处理结果决定是否继续发送数据包。

static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
  int ret;
  //通过BPF_CGROUP_RUN_PROG_INET_EGRESS调用与cgroup相关的BPF程序
  ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
  switch (ret) {
  case NET_XMIT_SUCCESS:
  //成功,发送数据包return __ip_finish_output(net, sk, skb);
  case NET_XMIT_CN:
  //数据包在拥塞情况下也允许发送数据包return __ip_finish_output(net, sk, skb) ? : ret;
  default:
  //不允许发送,释放数据包资源,标记为被BPF CGROUP 规则丢弃SKB_DROP_REASON_BPF_CGROUP_EGRESSkfree_skb_reason(skb, SKB_DROP_REASON_BPF_CGROUP_EGRESS);return ret;
  }
}
__ip_finish_output()

处理 IPv4 数据包的实际发送逻辑,负责根据数据包的属性(如 MTU 和分片需求)决定是否进行分片、是否使用大包分割 (GSO),以及是否调用适当的下层函数来完成数据包的发送。

static int __ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
  unsigned int mtu;
  //定义CONFIG_NETFILTER以及CONFIG_XFRM且目的ip的xfrm字段存在
\#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
  /* Policy lookup after SNAT yielded a new policy */
  if (skb_dst(skb)->xfrm) {//标记IPSKB_REROUTED重新路由IPCB(skb)->flags |= IPSKB_REROUTED;//输出到下一个目的地return dst_output(net, sk, skb);
  }
\#endif
//获取当前目标路径的最大传输单元MTU,可以发送的最大数据包大小
  mtu = ip_skb_dst_mtu(sk, skb);
  //如果该数据包是GSO(大分组分段),GSO允许数据包在网络协议栈中尽可能大,提升速率,在实际传输前进行分段
  if (skb_is_gso(skb))return ip_finish_output_gso(net, sk, skb, mtu);
  //数据包长度大于MTU/IP数据包选项要求进行分片
  if (skb->len > mtu || IPCB(skb)->frag_max_size)
  //分片处理return ip_fragment(net, sk, skb, mtu, ip_finish_output2);//不进行分片
  return ip_finish_output2(net, sk, skb);
}
ip_finish_output2()

IP数据包发送流程的最后一步,负责将数据包通过相应的网络接口传输出去。涉及到多播、广播、邻居缓存、隧道重定向等处理。

static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
  //从skb中提取目标条目
  struct dst_entry *dst = skb_dst(skb);
  //将目标路径条目转换成路由表条目
  struct rtable *rt = (struct rtable *)dst;
  //获取数据包目标网络设备
  struct net_device *dev = dst->dev;
  //计算链路层预留空间长度,hh_len链路层header信息
  unsigned int hh_len = LL_RESERVED_SPACE(dev);
  struct neighbour *neigh;
  //ipv6网关
  bool is_v6gw = false;
  //多播
  if (rt->rt_type == RTN_MULTICAST) {IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
  } //广播
  else if (rt->rt_type == RTN_BROADCAST)IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);
  //skb的头部可用空间是否足以存放链路层头部信息
  if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {//扩展空间
​    skb = skb_expand_head(skb, hh_len);if (!skb)return -ENOMEM;
  }
  //是否需要通过隧道传输
  if (lwtunnel_xmit_redirect(dst->lwtstate)) {//发送数据包int res = lwtunnel_xmit(skb);if (res < 0 || res == LWTUNNEL_XMIT_DONE)return res;
  }
  rcu_read_lock_bh();
  //获取下一跳邻居信息,并判断是否是ipv6网关
  neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
  if (!IS_ERR(neigh)) {int res;//确认邻居信息sock_confirm_neigh(skb, neigh);/* if crossing protocols, can not use the cached header */
​    res = neigh_output(neigh, skb, is_v6gw);rcu_read_unlock_bh();return res;
  }
  rcu_read_unlock_bh();
  //没有找到邻居条目
  net_dbg_ratelimited("%s: No header cache and no neighbour!\n",__func__);
  //丢弃数据包
  kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
  return -EINVAL;
}

以上是关于IP数据包经过netfilter框架收包、发包、转发路径源码分析,当我们在用户态执行iptables命令时,以INPUT链为例,iptables -A INPUT -s 192.168.1.1 -j DROP,经过一系列步骤到达netfilter框架,netfilter将新的规则添加到 INPUT 链,当新的数据包到达网络协议栈时会与该链中新增的规则进行逐条匹配,根据匹配的规则定义其动作,如ACCEPTDROPREJECTDROP,进行处理,默认策略ACCEPT

以上见解如有错误,欢迎指正~~,未完待续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2160234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

❤Node11-登录人token信息接口

❤Node11-登录人token信息接口​ 上一章我们已经从登录部分拿到了用户的登录jwt返回的token信息&#xff0c;接下来我们就通过token来换取用户信息 这里我们可以将其理解为一种加密以及解密的思想来思考这个jwt和token的关系&#xff0c;token就是一个加密的字符串&#xff0c…

【JavaEE】——内存可见性问题

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯&#xff0c;你们的点赞收藏是我前进最大的动力&#xff01;&#xff01;希望本文内容能够帮助到你&#xff01; 目录 一&#xff1a;内存可见性问题 1&#xff1a;代码解释 2&#xff1a;结果分析 &#xff08;1&#xf…

《现代畜牧兽医》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《现代畜牧兽医》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定 学术期刊。 问&#xff1a;《现代畜牧兽医》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;辽宁省科学技术协会 …

vue2实现提取字符串数字并修改数字样式(正则表达式)

如果你要在循环中提取 item.companyName 中的数字&#xff0c;并且希望为这些数字改变颜色和边距&#xff0c;可以对每个 item 进行处理。此处是一个实现示例&#xff1a; <template> <div> <div class"box" v-for"(item, index) in coldBase…

学校气膜体育馆:低成本、高效率的灵活运动空间—轻空间

在当前教育设施的升级中&#xff0c;传统体育馆的建设往往面临长时间、高成本、以及繁琐的审批流程等诸多挑战。然而&#xff0c;学校无需再为这些问题烦恼&#xff0c;只需选择气膜结构的体育馆&#xff0c;就能快速、高效地解决体育场地需求。气膜体育馆凭借其灵活的设计和高…

Java项目实战II基于SSM的国外摇滚乐队交流和周边售卖系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 随着互联网技术的飞速发展&#xff0c;信息传播的广度和深度不断拓展&#xff0c;为各行业的创新发展…

二分查找算法(4) _搜索插入位置

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二分查找算法(4) _搜索插入位置 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 …

太爱这5本书了,建议所有大模型人去翻烂它❗

要说现在最热门的技术&#xff0c;可谓非大模型莫属&#xff01; 不少小伙伴都想要学习大模型技术&#xff0c;转战AI领域&#xff0c;以适应未来的大趋势&#xff0c;寻求更有前景的发展~~ 然而&#xff0c;在学习大模型技术这条道路上&#xff0c;却不知道如何进行系统的学…

无人机飞手培训校企合作特训技术详解

随着无人机技术的飞速发展&#xff0c;其在航拍、农业、测绘、救援等多个领域的应用日益广泛&#xff0c;市场对高素质无人机飞手的需求急剧增加。为满足这一需求&#xff0c;促进教育与产业深度融合&#xff0c;无人机飞手培训校企合作模式应运而生。本文将从确定合作目标、共…

可视化大屏看阿里,阿里出品,必属精品。

阿里云有自己的可视化平台——dataV&#xff0c;经常会出一些高颜值、强交互的大屏&#xff0c;本期为大家分享一波。

HTML、CSS

初识web前端 web标准 Web标准也称为网页标准&#xff0c;由一系列的标准组成&#xff0c;大部分由W3C (World Wide Web Consortium&#xff0c;万维网联盟) 负责制定。三个组成部分: HTML: 负责网页的结构(页面元素和内容)。CSS: 负责网页的表现(页面元素的外观、位置等页面样…

Tableau|一入门

一 什么是BI工具 BI 工具即商业智能&#xff08;Business Intelligence&#xff09;工具&#xff0c;是一种用于收集、整理、分析和展示企业数据的软件系统&#xff0c;其主要目的是帮助企业用户更好地理解和利用数据&#xff0c;以支持决策制定。 主要功能&#xff1a; 1.数据…

Vue3使用通信组件库mitt作为事件总线实现跨组件通信

mitt 介绍: Mitt 是一个在 Vue.js 应用程序中使用的小型事件总线库。该库允许组件进行通信&#xff0c;而不必过度依赖父级或子级组件之间的 props。 先看项目用例&#xff1a; 【 以下转载自&#xff1a;https://blog.csdn.net/yuanlong12178/article/details/139579299 】…

虚拟机安装xubuntu

新建一个新的虚拟机&#xff0c;选择自定义安装 默认下一步 选择稍后安装操作系统 选择所要创建的系统及版本 填写虚拟机的名称及创建的虚拟机保存的位置 选择处理器和内核的数量 处理器数量指的是&#xff1a;虚拟的CPU数量。 每个处理器的内核数量指的是&#xff1a;虚拟CPU…

Ubuntu 24.04.1 LTS 安装 node 16.20.2环境

目录 step1&#xff1a;确认版本 step2&#xff1a;选择方式 step3&#xff1a;二进制文件安装 step1&#xff1a;确认版本 不同的版本情况可能有稍许不同&#xff0c;尽可能环境安装前版本保持一致&#xff1b; lsb_release -a 或者 cat /etc/os-release 可以查看版本信…

【机器学习】---元强化学习

目录 1. 元学习简介1.1 什么是元学习&#xff1f;1.2 元学习的应用 2. 强化学习基础2.1 什么是强化学习&#xff1f;2.2 强化学习的基本框架2.3 深度强化学习 3. 元强化学习的概念与工作原理3.1 元强化学习是什么&#xff1f;3.2 元强化学习与普通强化学习的区别 4. 元强化学习…

Arthas ognl(执行ognl表达式)

文章目录 二、命令列表2.1 jvm相关命令2.1.12 ognl&#xff08;执行ognl表达式&#xff09;举例1&#xff1a;获取静态属性举例2&#xff1a;调用静态方法 二、命令列表 2.1 jvm相关命令 2.1.12 ognl&#xff08;执行ognl表达式&#xff09; 使用场景&#xff1a; Arthas 的 …

【Vue系列五】—Vue学习历程的知识分享!

前言 本篇文章讲述前端工程化从模块化到如今的脚手架的发展&#xff0c;以及Webpack、Vue脚手架的详解&#xff01; 一、模块化 模块化就是把单独的功能封装到模块&#xff08;文件&#xff09;中&#xff0c;模块之间相互隔离&#xff0c;但可以通过特定的接口公开内部成员…

黑马智数Day2

表单基础校验实现 基础双向绑定 v-model <el-input v-model"formData.username" /> <script> export default {name: Login,data() {return {formData: {username: ,password: ,remember: }}} } </script> 表单校验配置 按照业务要求编写校验规…

231. 2 的幂 简单递归 python除法的类型

已解答 简单 相关标签 相关企业 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 示例 1&#xff1a; 输入&…