www 发布的文章

Warp+非常好用,速度快,又免费(名义上的收费,但事实上的免费),但是源IP默认为相邻的地区,在使用一些应用时会受目标站点的限制,比如访问: ChatGPT、Netflix等站点,现有终于有大神解决了该问题。

下载

项目源代码:

https://github.com/bepass-org/warp-plus

可执行文件下载地址:
warp-plus

使用

warp -b 0.0.0.0: 本地端口号 -e 优选 ip: 端口号 -k g8lG9B50-Ge346l2I-5FeYv182 -country 国家代码 -cfon
  • 本地端口号:本机Socks代理端口号
  • 优选 ip: 端口号:使用warp plus的优选Endpoint IP及端口,通过优选工具选取最优IP及端口
  • warp plus密钥:填写Warp+ key,参考get warp plus key
  • 国家代码:填写你想设定的国家地区代码,如下:

    Austria (AT)
    Belgium (BE)
    Bulgaria (BG)
    Brazil (BR)
    Canada (CA)
    Switzerland (CH)
    Czech Republic (CZ)
    Germany (DE)
    Denmark (DK)
    Estonia (EE)
    Spain (ES)
    Finland (FI)
    France (FR)
    United Kingdom (GB)
    Hungary (HU)
    Ireland (IE)
    India (IN)
    Italy (IT)
    Japan (JP)
    Latvia (LV)
    Netherlands (NL)
    Norway (NO)
    Poland (PL)
    Romania (RO)
    Serbia (RS)
    Sweden (SE)
    Singapore (SG)
    Slovakia (SK)
    Ukraine (UA)
    United States (US)

    应用示例:

    warp -b 0.0.0.0:12345 -e 162.159.192.100:987 -k g8lG9B50-Ge346l2I-5FeYv182 -country JP -cfon

    执行后,出现如下提示,说明建立成功:
    2024-04-15T06:13:54.png

保持该窗口不关闭,用代理软件或者浏览器访问本地socks代理127.0.0.1:12345即可上网,并实现IP固定为日本地区。

在国内86手机号有概率性的收不到tg验证码短信,tg有Login Email功能,但似乎只在内测,没有全部放开,这也催生了很多卖Login Email功能号的生意,既然有人干这门生意,那就说明有门道能开通,经过尝试如下方法可行,现分享:

  • 给Volunteer Support留言开通
Setting - Ask a Question - Ask a Volunteer

根据提示,用英文留言希望能支持开通,没有回复,但过一两天后Logout再Login就成了

  • Youter分享的通过刷Login Code触发Login Email功能

就是先用tg登录,再用tgx不停的登录退出,只选择用短信收接验证码登录,个人没有尝试过,按理论上存在一定的风险,仅供参考:

Telegram开通Email登录

嵌入式Linux内核修改转发skb网络数据包

实际的项目软件开过程中,碰到一个需求,需要修改以太网接收到的数据包,主要有两点需求:

  • L2层特定自定义协议包修改目的MAC地址
  • 针对特定端口的UDP包,修改其目的IP后,再接交给上层(或在Bridge转发之前)处理

方案评估

第一点需求,修改L2层的MAC地址,可采用:

  • 采用ebtable的DNAT修改L2层MAC地址
  • 通过网络驱动传参修改

第二点需求,修改Bridge转发之前的特定端口包目的IP,可采用:

  • 用iptables的INT_NET_PREROUTING Hook,结合br_netfilter模块的bridge-nf-call-iptables功能实现

    /proc/sys/net/bridge/bridge-nf-call-iptables
  • 编写一个netfilter hook内核模块,通过bridge NF_BR_PRE_ROUTING hook点特殊处理

最终采用的方案

上述两点的两个方案,理论上都是第一条比较优雅,但是现实总是残酷的,在嵌入式系统上,芯片资源有限,CPU\RAM\Flash资源受限的情况下,无论是ebtables或是iptables都太重了,特别还要考虑转发执行效率尽量的减少CPU处理,最终都采用了第二种策略。

实现注意点

  • 网络驱动的代码netif_rx(skb);都处于中断的下半区,代码需要高效简短,否则严重影响CPU。
  • 编写netfilter hook模块时,挂载在NF_BR_PRE_ROUTING hook处理节点,处于L2层,收到的skb数据包,无论是网络层、还是传输层指针都是无效的,不能直接引用
  • 挂载点协议选择NFPROTO_BRIDGE

    static struct nf_hook_ops nfho = {
      .hook = hook_func,
      .hooknum = NF_BR_PRE_ROUTING,
      .pf = NFPROTO_BRIDGE,
      .priority = NF_BR_PRI_FIRST,
    };
  • 修改了UDP包的目的IP后,需要重新计算UDP及IP Header的checksum;此外,这里仅修改目的IP,不需要重新全部计算,可以使用inet_proto_csum_replace4只作替代更新,提高效率
  • 目的IP如果修改为组播或广播包,则需要对应的修改eth header的目的MAC地址,否则包不会经过Bridge广播,从L2层上看还是会传给NF_BR_LOCAL_IN

参考代码及资料

  1. 参考代码1

    /*
     * Copyright (c) 2007-2012 Nicira, Inc.
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of version 2 of the GNU General Public
     * License as published by the Free Software Foundation.
     *
     * This program is distributed in the hope that it will be useful, but
     * WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     * General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
     * 02110-1301, USA
     */
    
    #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
    
    #include <linux/skbuff.h>
    #include <linux/in.h>
    #include <linux/ip.h>
    #include <linux/openvswitch.h>
    #include <linux/tcp.h>
    #include <linux/udp.h>
    #include <linux/in6.h>
    #include <linux/if_arp.h>
    #include <linux/if_vlan.h>
    #include <net/ip.h>
    #include <net/ipv6.h>
    #include <net/checksum.h>
    #include <net/dsfield.h>
    
    #include "checksum.h"
    #include "datapath.h"
    #include "vlan.h"
    #include "vport.h"
    
    static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                   const struct nlattr *attr, int len,
                   struct ovs_key_ipv4_tunnel *tun_key, bool keep_skb);
    
    static int make_writable(struct sk_buff *skb, int write_len)
    {
     if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
         return 0;
    
     return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
    }
    
    /* remove VLAN header from packet and update csum accordingly. */
    static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
    {
     struct vlan_hdr *vhdr;
     int err;
    
     err = make_writable(skb, VLAN_ETH_HLEN);
     if (unlikely(err))
         return err;
    
     if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
         skb->csum = csum_sub(skb->csum, csum_partial(skb->data
                     + ETH_HLEN, VLAN_HLEN, 0));
    
     vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
     *current_tci = vhdr->h_vlan_TCI;
    
     memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
     __skb_pull(skb, VLAN_HLEN);
    
     vlan_set_encap_proto(skb, vhdr);
     skb->mac_header += VLAN_HLEN;
     skb_reset_mac_len(skb);
    
     return 0;
    }
    
    static int pop_vlan(struct sk_buff *skb)
    {
     __be16 tci;
     int err;
    
     if (likely(vlan_tx_tag_present(skb))) {
         vlan_set_tci(skb, 0);
     } else {
         if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
                  skb->len < VLAN_ETH_HLEN))
             return 0;
    
         err = __pop_vlan_tci(skb, &tci);
         if (err)
             return err;
     }
     /* move next vlan tag to hw accel tag */
     if (likely(skb->protocol != htons(ETH_P_8021Q) ||
            skb->len < VLAN_ETH_HLEN))
         return 0;
    
     err = __pop_vlan_tci(skb, &tci);
     if (unlikely(err))
         return err;
    
     __vlan_hwaccel_put_tag(skb, ntohs(tci));
     return 0;
    }
    
    static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan)
    {
     if (unlikely(vlan_tx_tag_present(skb))) {
         u16 current_tag;
    
         /* push down current VLAN tag */
         current_tag = vlan_tx_tag_get(skb);
    
         if (!__vlan_put_tag(skb, current_tag))
             return -ENOMEM;
    
         if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
             skb->csum = csum_add(skb->csum, csum_partial(skb->data
                     + ETH_HLEN, VLAN_HLEN, 0));
    
     }
     __vlan_hwaccel_put_tag(skb, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
     return 0;
    }
    
    static int set_eth_addr(struct sk_buff *skb,
             const struct ovs_key_ethernet *eth_key)
    {
     int err;
     err = make_writable(skb, ETH_HLEN);
     if (unlikely(err))
         return err;
    
     memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_ALEN);
     memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_ALEN);
    
     return 0;
    }
    
    static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
                 __be32 *addr, __be32 new_addr)
    {
     int transport_len = skb->len - skb_transport_offset(skb);
    
     if (nh->protocol == IPPROTO_TCP) {
         if (likely(transport_len >= sizeof(struct tcphdr)))
             inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
                          *addr, new_addr, 1);
     } else if (nh->protocol == IPPROTO_UDP) {
         if (likely(transport_len >= sizeof(struct udphdr))) {
             struct udphdr *uh = udp_hdr(skb);
    
             if (uh->check ||
                 get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
                 inet_proto_csum_replace4(&uh->check, skb,
                              *addr, new_addr, 1);
                 if (!uh->check)
                     uh->check = CSUM_MANGLED_0;
             }
         }
     }
    
     csum_replace4(&nh->check, *addr, new_addr);
     skb_clear_rxhash(skb);
     *addr = new_addr;
    }
    
    static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
                  __be32 addr[4], const __be32 new_addr[4])
    {
     int transport_len = skb->len - skb_transport_offset(skb);
    
     if (l4_proto == IPPROTO_TCP) {
         if (likely(transport_len >= sizeof(struct tcphdr)))
             inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
                           addr, new_addr, 1);
     } else if (l4_proto == IPPROTO_UDP) {
         if (likely(transport_len >= sizeof(struct udphdr))) {
             struct udphdr *uh = udp_hdr(skb);
    
             if (uh->check ||
                 get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
                 inet_proto_csum_replace16(&uh->check, skb,
                               addr, new_addr, 1);
                 if (!uh->check)
                     uh->check = CSUM_MANGLED_0;
             }
         }
     }
    }
    
    static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
               __be32 addr[4], const __be32 new_addr[4],
               bool recalculate_csum)
    {
     if (recalculate_csum)
         update_ipv6_checksum(skb, l4_proto, addr, new_addr);
    
     skb_clear_rxhash(skb);
     memcpy(addr, new_addr, sizeof(__be32[4]));
    }
    
    static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc)
    {
     nh->priority = tc >> 4;
     nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4);
    }
    
    static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl)
    {
     nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16;
     nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8;
     nh->flow_lbl[2] = fl & 0x000000FF;
    }
    
    static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
    {
     csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
     nh->ttl = new_ttl;
    }
    
    static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
    {
     struct iphdr *nh;
     int err;
    
     err = make_writable(skb, skb_network_offset(skb) +
                  sizeof(struct iphdr));
     if (unlikely(err))
         return err;
    
     nh = ip_hdr(skb);
    
     if (ipv4_key->ipv4_src != nh->saddr)
         set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src);
    
     if (ipv4_key->ipv4_dst != nh->daddr)
         set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst);
    
     if (ipv4_key->ipv4_tos != nh->tos)
         ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos);
    
     if (ipv4_key->ipv4_ttl != nh->ttl)
         set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl);
    
     return 0;
    }
    
    static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
    {
     struct ipv6hdr *nh;
     int err;
     __be32 *saddr;
     __be32 *daddr;
    
     err = make_writable(skb, skb_network_offset(skb) +
                 sizeof(struct ipv6hdr));
     if (unlikely(err))
         return err;
    
     nh = ipv6_hdr(skb);
     saddr = (__be32 *)&nh->saddr;
     daddr = (__be32 *)&nh->daddr;
    
     if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src)))
         set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr,
                   ipv6_key->ipv6_src, true);
    
     if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) {
         unsigned int offset = 0;
         int flags = OVS_IP6T_FH_F_SKIP_RH;
         bool recalc_csum = true;
    
         if (ipv6_ext_hdr(nh->nexthdr))
             recalc_csum = ipv6_find_hdr(skb, &offset,
                             NEXTHDR_ROUTING, NULL,
                             &flags) != NEXTHDR_ROUTING;
    
         set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr,
                   ipv6_key->ipv6_dst, recalc_csum);
     }
    
     set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
     set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label));
     nh->hop_limit = ipv6_key->ipv6_hlimit;
    
     return 0;
    }
    
    /* Must follow make_writable() since that can move the skb data. */
    static void set_tp_port(struct sk_buff *skb, __be16 *port,
              __be16 new_port, __sum16 *check)
    {
     inet_proto_csum_replace2(check, skb, *port, new_port, 0);
     *port = new_port;
     skb_clear_rxhash(skb);
    }
    
    static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
    {
     struct udphdr *uh = udp_hdr(skb);
    
     if (uh->check && get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
         set_tp_port(skb, port, new_port, &uh->check);
    
         if (!uh->check)
             uh->check = CSUM_MANGLED_0;
     } else {
         *port = new_port;
         skb_clear_rxhash(skb);
     }
    }
    
    static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key)
    {
     struct udphdr *uh;
     int err;
    
     err = make_writable(skb, skb_transport_offset(skb) +
                  sizeof(struct udphdr));
     if (unlikely(err))
         return err;
    
     uh = udp_hdr(skb);
     if (udp_port_key->udp_src != uh->source)
         set_udp_port(skb, &uh->source, udp_port_key->udp_src);
    
     if (udp_port_key->udp_dst != uh->dest)
         set_udp_port(skb, &uh->dest, udp_port_key->udp_dst);
    
     return 0;
    }
    
    static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
    {
     struct tcphdr *th;
     int err;
    
     err = make_writable(skb, skb_transport_offset(skb) +
                  sizeof(struct tcphdr));
     if (unlikely(err))
         return err;
    
     th = tcp_hdr(skb);
     if (tcp_port_key->tcp_src != th->source)
         set_tp_port(skb, &th->source, tcp_port_key->tcp_src, &th->check);
    
     if (tcp_port_key->tcp_dst != th->dest)
         set_tp_port(skb, &th->dest, tcp_port_key->tcp_dst, &th->check);
    
     return 0;
    }
    
    static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
    {
     struct vport *vport;
    
     if (unlikely(!skb))
         return -ENOMEM;
    
     vport = ovs_vport_rcu(dp, out_port);
     if (unlikely(!vport)) {
         kfree_skb(skb);
         return -ENODEV;
     }
    
     ovs_vport_send(vport, skb);
     return 0;
    }
    
    static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                 const struct nlattr *attr)
    {
     struct dp_upcall_info upcall;
     const struct nlattr *a;
     int rem;
    
     upcall.cmd = OVS_PACKET_CMD_ACTION;
     upcall.key = &OVS_CB(skb)->flow->key;
     upcall.userdata = NULL;
     upcall.pid = 0;
    
     for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
          a = nla_next(a, &rem)) {
         switch (nla_type(a)) {
         case OVS_USERSPACE_ATTR_USERDATA:
             upcall.userdata = a;
             break;
    
         case OVS_USERSPACE_ATTR_PID:
             upcall.pid = nla_get_u32(a);
             break;
         }
     }
    
     return ovs_dp_upcall(dp, skb, &upcall);
    }
    
    static int sample(struct datapath *dp, struct sk_buff *skb,
           const struct nlattr *attr,
           struct ovs_key_ipv4_tunnel *tun_key)
    {
     const struct nlattr *acts_list = NULL;
     const struct nlattr *a;
     int rem;
    
     for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
          a = nla_next(a, &rem)) {
         switch (nla_type(a)) {
         case OVS_SAMPLE_ATTR_PROBABILITY:
             if (net_random() >= nla_get_u32(a))
                 return 0;
             break;
    
         case OVS_SAMPLE_ATTR_ACTIONS:
             acts_list = a;
             break;
         }
     }
    
     return do_execute_actions(dp, skb, nla_data(acts_list),
                   nla_len(acts_list), tun_key, true);
    }
    
    static int execute_set_action(struct sk_buff *skb,
                  const struct nlattr *nested_attr,
                  struct ovs_key_ipv4_tunnel *tun_key)
    {
     int err = 0;
    
     switch (nla_type(nested_attr)) {
     case OVS_KEY_ATTR_PRIORITY:
         skb->priority = nla_get_u32(nested_attr);
         break;
    
     case OVS_KEY_ATTR_TUN_ID:
         /* If we're only using the TUN_ID action, store the value in a
          * temporary instance of struct ovs_key_ipv4_tunnel on the stack.
          * If both IPV4_TUNNEL and TUN_ID are being used together we
          * can't write into the IPV4_TUNNEL action, so make a copy and
          * write into that version.
          */
         if (!OVS_CB(skb)->tun_key)
             memset(tun_key, 0, sizeof(*tun_key));
         else if (OVS_CB(skb)->tun_key != tun_key)
             memcpy(tun_key, OVS_CB(skb)->tun_key, sizeof(*tun_key));
         OVS_CB(skb)->tun_key = tun_key;
    
         OVS_CB(skb)->tun_key->tun_id = nla_get_be64(nested_attr);
         break;
    
     case OVS_KEY_ATTR_IPV4_TUNNEL:
         OVS_CB(skb)->tun_key = nla_data(nested_attr);
         break;
    
     case OVS_KEY_ATTR_ETHERNET:
         err = set_eth_addr(skb, nla_data(nested_attr));
         break;
    
     case OVS_KEY_ATTR_IPV4:
         err = set_ipv4(skb, nla_data(nested_attr));
         break;
    
     case OVS_KEY_ATTR_IPV6:
         err = set_ipv6(skb, nla_data(nested_attr));
         break;
    
     case OVS_KEY_ATTR_TCP:
         err = set_tcp(skb, nla_data(nested_attr));
         break;
    
     case OVS_KEY_ATTR_UDP:
         err = set_udp(skb, nla_data(nested_attr));
         break;
     }
    
     return err;
    }
    
    /* Execute a list of actions against 'skb'. */
    static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
             const struct nlattr *attr, int len,
             struct ovs_key_ipv4_tunnel *tun_key, bool keep_skb)
    {
     /* Every output action needs a separate clone of 'skb', but the common
      * case is just a single output action, so that doing a clone and
      * then freeing the original skbuff is wasteful.  So the following code
      * is slightly obscure just to avoid that. */
     int prev_port = -1;
     const struct nlattr *a;
     int rem;
    
     for (a = attr, rem = len; rem > 0;
          a = nla_next(a, &rem)) {
         int err = 0;
    
         if (prev_port != -1) {
             do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
             prev_port = -1;
         }
    
         switch (nla_type(a)) {
         case OVS_ACTION_ATTR_OUTPUT:
             prev_port = nla_get_u32(a);
             break;
    
         case OVS_ACTION_ATTR_USERSPACE:
             output_userspace(dp, skb, a);
             break;
    
         case OVS_ACTION_ATTR_PUSH_VLAN:
             err = push_vlan(skb, nla_data(a));
             if (unlikely(err)) /* skb already freed. */
                 return err;
             break;
    
         case OVS_ACTION_ATTR_POP_VLAN:
             err = pop_vlan(skb);
             break;
    
         case OVS_ACTION_ATTR_SET:
             err = execute_set_action(skb, nla_data(a), tun_key);
             break;
    
         case OVS_ACTION_ATTR_SAMPLE:
             err = sample(dp, skb, a, tun_key);
             break;
         }
    
         if (unlikely(err)) {
             kfree_skb(skb);
             return err;
         }
     }
    
     if (prev_port != -1) {
         if (keep_skb)
             skb = skb_clone(skb, GFP_ATOMIC);
    
         do_output(dp, skb, prev_port);
     } else if (!keep_skb)
         consume_skb(skb);
    
     return 0;
    }
    
    /* We limit the number of times that we pass into execute_actions()
     * to avoid blowing out the stack in the event that we have a loop. */
    #define MAX_LOOPS 5
    
    struct loop_counter {
     u8 count;        /* Count. */
     bool looping;        /* Loop detected? */
    };
    
    static DEFINE_PER_CPU(struct loop_counter, loop_counters);
    
    static int loop_suppress(struct datapath *dp, struct sw_flow_actions *actions)
    {
     if (net_ratelimit())
         pr_warn("%s: flow looped %d times, dropping\n",
                 ovs_dp_name(dp), MAX_LOOPS);
     actions->actions_len = 0;
     return -ELOOP;
    }
    
    /* Execute a list of actions against 'skb'. */
    int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb)
    {
     struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
     struct loop_counter *loop;
     int error;
     struct ovs_key_ipv4_tunnel tun_key;
    
     /* Check whether we've looped too much. */
     loop = &__get_cpu_var(loop_counters);
     if (unlikely(++loop->count > MAX_LOOPS))
         loop->looping = true;
     if (unlikely(loop->looping)) {
         error = loop_suppress(dp, acts);
         kfree_skb(skb);
         goto out_loop;
     }
    
     OVS_CB(skb)->tun_key = NULL;
     error = do_execute_actions(dp, skb, acts->actions,
                      acts->actions_len, &tun_key, false);
    
     /* Check whether sub-actions looped too much. */
     if (unlikely(loop->looping))
         error = loop_suppress(dp, acts);
    
    out_loop:
     /* Decrement loop counter. */
     if (!--loop->count)
         loop->looping = false;
    
     return error;
    }
  2. 独立内核模块示例代码

    #include <linux/module.h>
    #include <linux/netfilter.h>
    #include <linux/netfilter_ipv4.h>
    #include <linux/ip.h>
    #include <linux/udp.h>
    #include <linux/skbuff.h>
    
    #define TARGET_PORT 12345  // 设置特定的目的端口
    #define MULTICAST_IP "224.0.0.1"  // 指定的组播IP地址
    
    // Netfilter钩子函数,用于处理入站的数据包
    static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
    {
     struct iphdr *ip_header;
     struct udphdr *udp_header;
    
     // 检查skb包是否为空
     if (!skb)
         return NF_ACCEPT;
    
     // 检查skb包是否包含IP头部
     if (!skb_network_header(skb))
         return NF_ACCEPT;
    
     // 获取IP头部和UDP头部
     ip_header = ip_hdr(skb);
     udp_header = (struct udphdr *)(skb_transport_header(skb) + ip_hdrlen(skb));
    
     // 检查是否为UDP协议和特定目的端口
     if (ip_header->protocol == IPPROTO_UDP && ntohs(udp_header->dest) == TARGET_PORT) {
         // 修改目的IP地址为指定的组播IP
         ip_header->daddr = in_aton(MULTICAST_IP);
    
         // 重新计算校验值
         udp_header->check = 0;
         udp_header->check = csum_tcpudp_magic(ip_header->saddr, ip_header->daddr,
                                                skb->len - ip_hdrlen(skb), IPPROTO_UDP, csum_partial(udp_header, skb->len - ip_hdrlen(skb), 0));
    
         // 更新校验和
         skb->ip_summed = CHECKSUM_NONE;
         skb->pkt_type = PACKET_MULTICAST;
    
         // 让tcp/ip协议栈处理skb包
         return NF_ACCEPT;
     }
    
     return NF_ACCEPT;
    }
    
    // 初始化和清理模块
    static struct nf_hook_ops nfho = {
     .hook = hook_func,
     .hooknum = NF_BR_PRE_ROUTING,
     .pf = NFPROTO_BRIDGE,
     .priority = NF_BR_PRI_FIRST,
    };
    
    static int __init my_module_init(void)
    {
     return nf_register_hook(&nfho);
    }
    
    static void __exit my_module_exit(void)
    {
     nf_unregister_hook(&nfho);
    }
    
    module_init(my_module_init);
    module_exit(my_module_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Kimson");
  3. linux内核中如何修改skb报文
  4. linux-2-6-network-stack-paper.pdf
  5. skbuff.pdf