分类 技术杂记 下的文章

现有一个简单的需求,需要用户弹窗选择一个铃音,要求在正式的确认之前就能播放对应的音乐作预听,微信小程序picker(mode = selector)可以实现选择确认功能,但只有点击确认后的bindchange事件,没有选择变化事件。根据微信文档,在mode = multiSelector时,有多了一个bindcolumnchange事件,灵光一闪,只有一列的multiSelector不就是selector?那不就可以把multiSelector当selector用?

wxml:

          <view class="option">铃声: 
          <picker mode="multiSelector" range="{{[ringtones]}}" data-alarmid="{{item.id}}" bindcolumnchange="bindRingtoneSelect" bindchange="bindRingtoneChange">
            <view class="picker">{{ringtones[item.ringtoneIndex]}} <text class="picker-selector"> ❯</text> </view> 
          </picker>
          </view>

特别注意其中:ringtones一维数组,调整为:[ringtones]引用。

js:

  bindRingtoneSelect: function(e) {
    proto.sendPlayMusic(e.detail.value);
  },

  bindRingtoneChange:function(e) {
    proto.sendSaveMusic(e.detail.value);
  },

实验效果完美解决~~

Debian默认root帐号shell无色彩解决

默认root帐号的shell无色彩

修改.bashrc文件为:

# ~/.bashrc: executed by bash(1) for non-login shells.

# Note: PS1 and umask are already set in /etc/profile. You should not
# need this unless you want different defaults for root.
 PS1='${debian_chroot:+($debian_chroot)}\h:\w\$ '
# umask 022

# You may uncomment the following lines if you want `ls' to be colorized:
 export LS_OPTIONS='--color=auto'
 eval "$(dircolors)"
 alias ls='ls $LS_OPTIONS'
 alias ll='ls $LS_OPTIONS -l'
 alias l='ls $LS_OPTIONS -lA'

# Some more alias to avoid making mistakes:
# alias rm='rm -i'
# alias cp='cp -i'
# alias mv='mv -i'

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    # We have color support; assume it's compliant with Ecma-48
    # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
    # a case would tend to support setf rather than setaf.)
    color_prompt=yes
    else
    color_prompt=
    fi
fi

if [ ! -z "$PS1" ]; then
    if [ "$color_prompt" = yes ]; then
        PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
    else
        PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
    fi
fi

FOFA搜索:

body="get all proxy from proxy pool"

可以得到很多爬虫维护的代理站点:

http://113.235.121.92:5010/
http://112.74.98.149:5010/
http://159.75.88.180:5010/
http://101.200.37.197:5010/
https://49.235.191.186/
...……

拉取一个:

http://113.235.121.92:5010/all/

可得:

[{"anonymous":"","check_count":60,"fail_count":0,"https":true,"last_status":true,"last_time":"2024-06-26 14:47:38","proxy":"47.96.176.130:59394","region":"\u4e2d\u56fd \u6d59\u6c5f \u676d\u5dde \u963f\u91cc\u4e91/\u7535\u4fe1/\u8054\u901a/\u79fb\u52a8/\u94c1\u901a/\u6559\u80b2\u7f51","source":"freeProxy11"},{"anonymous":"","check_count":2,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:42","proxy":"154.203.132.49:8080","region":"\u4e2d\u56fd \u9999\u6e2f  cloudinnovation.org","source":"freeProxy11"},{"anonymous":"","check_count":5,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:38","proxy":"183.60.141.41:443","region":"\u4e2d\u56fd \u5e7f\u4e1c \u4e1c\u839e \u7535\u4fe1","source":"freeProxy11"},{"anonymous":"","check_count":44,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:39","proxy":"178.48.68.61:18080","region":"\u5308\u7259\u5229 \u5308\u7259\u5229  upc.hu","source":"freeProxy11"},{"anonymous":"","check_count":13,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:45:44","proxy":"50.62.183.223:80","region":"\u7f8e\u56fd \u4e9a\u5229\u6851\u90a3\u5dde \u51e4\u51f0\u57ce godaddy.com","source":"freeProxy11"},{"anonymous":"","check_count":163,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:37","proxy":"116.63.129.202:6000","region":"\u4e2d\u56fd \u8d35\u5dde \u8d35\u9633 \u7535\u4fe1/\u8054\u901a/\u79fb\u52a8","source":"freeProxy11"},{"anonymous":"","check_count":1,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:45:51","proxy":"120.197.40.219:9002","region":"\u4e2d\u56fd \u5e7f\u4e1c \u5e7f\u5dde \u79fb\u52a8","source":"freeProxy11/freeProxy03"},{"anonymous":"","check_count":14,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:41","proxy":"62.236.76.83:8085","region":"\u82ac\u5170 \u65b0\u5730\u533a \u8d6b\u5c14\u8f9b\u57fa dna.fi","source":"freeProxy11"},{"anonymous":"","check_count":3,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:45:44","proxy":"142.93.142.36:9122","region":"\u8377\u5170 \u5317\u8377\u5170\u7701 \u963f\u59c6\u65af\u7279\u4e39 digitalocean.com","source":"freeProxy11"},{"anonymous":"","check_count":6,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:43","proxy":"154.203.132.55:8090","region":"\u4e2d\u56fd \u9999\u6e2f  cloudinnovation.org","source":"freeProxy11"},{"anonymous":"","check_count":4,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:37","proxy":"223.113.80.158:9091","region":"\u4e2d\u56fd \u6c5f\u82cf \u5f90\u5dde \u79fb\u52a8","source":"freeProxy11"},{"anonymous":"","check_count":46,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:39","proxy":"112.19.241.37:19999","region":"\u4e2d\u56fd \u56db\u5ddd \u6210\u90fd \u79fb\u52a8","source":"freeProxy11"},{"anonymous":"","check_count":2,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:37","proxy":"51.89.73.162:80","region":"\u5fb7\u56fd \u9ed1\u68ee\u5dde \u5170\u6cb3\u7554\u6797\u5821 ovh.com","source":"freeProxy11"},{"anonymous":"","check_count":5,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:43","proxy":"154.203.132.55:8080","region":"\u4e2d\u56fd \u9999\u6e2f  cloudinnovation.org","source":"freeProxy11"},{"anonymous":"","check_count":7,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:37","proxy":"60.12.168.114:9002","region":"\u4e2d\u56fd \u6d59\u6c5f \u53f0\u5dde \u8054\u901a","source":"freeProxy03"},{"anonymous":"","check_count":2,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:40","proxy":"8.209.253.237:8080","region":"\u65e5\u672c \u4e1c\u4eac\u90fd \u4e1c\u4eac \u963f\u91cc\u4e91","source":"freeProxy07"},{"anonymous":"","check_count":9,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:47:37","proxy":"111.160.204.146:9091","region":"\u4e2d\u56fd \u5929\u6d25 \u5929\u6d25 \u8054\u901a","source":"freeProxy11"},{"anonymous":"","check_count":3,"fail_count":0,"https":false,"last_status":true,"last_time":"2024-06-26 14:45:54","proxy":"161.34.40.109:3128","region":"\u65e5\u672c \u65e5\u672c  sphere.ne.jp","source":"freeProxy11"},{"anonymous":"","check_count":4,"fail_count":0,"https":true,"last_status":true,"last_time":"2024-06-26 14:45:48","proxy":"160.248.93.84:3128","region":"\u65e5\u672c \u65e5\u672c  sphere.ne.jp","source":"freeProxy11"}]

验证

#!/bin/bash

# 代理列表
proxies=(
  "47.96.176.130:59394"
  "178.48.68.61:18080"
  "91.148.127.162:8080"
  "50.62.183.223:80"
  "116.63.129.202:6000"
  "120.197.40.219:9002"
  "212.127.93.185:8081"
  "154.203.132.55:8090"
  "142.93.142.36:9122"
  "223.113.80.158:9091"
  "112.19.241.37:19999"
  "51.89.73.162:80"
  "154.203.132.55:8080"
  "49.13.9.253:80"
  "79.110.196.145:8081"
  "60.12.168.114:9002"
  "111.160.204.146:9091"
  "91.148.126.189:8080"
  "161.34.40.109:3128"
  "160.248.93.84:3128"
  "8.219.97.248:80"
  "103.153.154.6:80"
)

# 目标URL
url="http://www.baidu.com"

# 检查代理是否有效
for proxy in "${proxies[@]}"; do
  # 通过代理发送请求
  response=$(curl -s -o /dev/null -w "%{http_code}" -x "$proxy" "$url")
  
  # 检查响应状态码
  if [ "$response" -eq 200 ]; then
    echo "Proxy $proxy is valid."
  else
    echo "Proxy $proxy is invalid."
  fi
done

结果:

Proxy 47.96.176.130:59394 is valid.
Proxy 178.48.68.61:18080 is valid.
Proxy 91.148.127.162:8080 is valid.
Proxy 50.62.183.223:80 is valid.
Proxy 116.63.129.202:6000 is valid.
Proxy 120.197.40.219:9002 is valid.
Proxy 212.127.93.185:8081 is valid.
Proxy 154.203.132.55:8090 is valid.
Proxy 142.93.142.36:9122 is valid.
Proxy 223.113.80.158:9091 is valid.
Proxy 112.19.241.37:19999 is valid.
Proxy 51.89.73.162:80 is valid.
Proxy 154.203.132.55:8080 is valid.
Proxy 49.13.9.253:80 is invalid.
Proxy 79.110.196.145:8081 is valid.
Proxy 60.12.168.114:9002 is valid.
Proxy 111.160.204.146:9091 is valid.
Proxy 91.148.126.189:8080 is invalid.
Proxy 161.34.40.109:3128 is invalid.
Proxy 160.248.93.84:3128 is valid.
Proxy 8.219.97.248:80 is valid.
Proxy 103.153.154.6:80 is valid.

总结

  1. 可以通过脚本自动获取并验证更新代理节点
  2. 还有更多的搜索方法:

      protocol=="socks5" && "Version:5 Method:No Authentication(0x00)" && after="1900-01-01" && country="CN"
     "“89e3175.js”" 或title="代理池网页管理界面"

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固定为日本地区。

嵌入式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