diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 360635bb4..a75508602 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -17,18 +17,22 @@ jobs: runs-on: self-hosted env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true steps: - - uses: actions/checkout@v2 - - name: make + - name: Checkout Code + uses: actions/checkout@v3 + - name: build run: make -j build-all: runs-on: self-hosted env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true steps: - - uses: actions/checkout@v2 - - name: config + - name: Checkout Code + uses: actions/checkout@v3 + - name: Config run: sed -i 's/=n$/=y/' config.mk - - name: make + - name: build run: make -j diff --git a/.github/workflows/run.yaml b/.github/workflows/run.yaml index 29b1a5423..f71248963 100644 --- a/.github/workflows/run.yaml +++ b/.github/workflows/run.yaml @@ -17,11 +17,13 @@ jobs: runs-on: self-hosted env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true steps: - - uses: actions/checkout@v2 - - name: make + - name: Checkout Code + uses: actions/checkout@v3 + - name: Build run: make -j - - name: install + - name: Install run: make install - - name: run-dpvs + - name: Run DPVS run: sudo dpvsci $(pwd)/bin/dpvs diff --git a/README.md b/README.md index 4a7c1fa7c..b3f678a5a 100644 --- a/README.md +++ b/README.md @@ -23,38 +23,45 @@ Several techniques are applied for high performance: Major features of `DPVS` including: -* *L4 Load Balancer*, including FNAT, DR, Tunnel, DNAT modes, etc. +* *L4 Load Balancer*, supports FNAT, DR, Tunnel and DNAT reverse proxy modes. +* *NAT64* mode for IPv6 quick adaption without changing backend server. * *SNAT* mode for Internet access from internal network. -* *NAT64* forwarding in FNAT mode for quick IPv6 adaptation without application changes. -* Different *schedule algorithms* like RR, WLC, WRR, MH(Maglev Hashing), Conhash(Consistent Hashing) etc. -* User-space *Lite IP stack* (IPv4/IPv6, Routing, ARP, Neighbor, ICMP ...). -* Support *KNI*, *VLAN*, *Bonding*, *Tunneling* for different IDC environment. -* Security aspect, support *TCP syn-proxy*, *Conn-Limit*, *black-list*, *white-list*. -* QoS: *Traffic Control*. +* Adequate *schedule algorithms* like RR, WLC, WRR, MH(Maglev Hash), Conhash(Consistent Hash), etc. +* User-space *lite network stack*: IPv4, IPv6, Routing, ARP, Neighbor, ICMP, LLDP, IPset, etc. +* Support *KNI*, *VLAN*, *Bonding*, *IP Tunnel* for different IDC environment. +* Security aspects support *TCP SYN-proxy*, *Allow/Deny ACL*. +* QoS features such as *Traffic Control*, *Concurrent Connection Limit*. +* Versatile tools, services can be configured with `dpip` `ipvsadm` command line tools, or from config files of `keepalived`, or via restful API provided by `dpvs-agent`. -`DPVS` feature modules are illustrated as following picture. +DPVS consists of the modules illustrated in the diagram below. -![modules](./pic/modules.png) +![modules](./pic/modules.svg) # Quick Start ## Test Environment -This *quick start* is tested with the environment below. +This *quick start* is performed in the environments described below. -* Linux Distribution: CentOS 7.2 -* Kernel: 3.10.0-327.el7.x86_64 -* CPU: Intel(R) Xeon(R) CPU E5-2650 v3 @ 2.30GHz +* Linux Distribution: CentOS 7.6 +* Kernel: 3.10.0-957.el7.x86_64 +* CPU: Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz * NIC: Intel Corporation Ethernet Controller 10-Gigabit X540-AT2 (rev 03) * Memory: 64G with two NUMA node. -* GCC: gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) +* GCC: 4.8.5 20150623 (Red Hat 4.8.5-36) +* Golang: go1.20.4 linux/amd64 (required only when CONFIG_DPVS_AGENT enabled). -Other environments should also be OK if DPDK works, please check [dpdk.org](http://www.dpdk.org) for more info. +Other environments should also be OK if DPDK works, please check [dpdk.org](http://www.dpdk.org) for more information. -* Please check this link for NICs supported by DPDK: http://dpdk.org/doc/nics. -* Note `flow control` ([rte_flow](http://dpdk.org/doc/guides/nics/overview.html#id1)) is needed for `FNAT` and `SNAT` mode with multi-cores. - -> Notes: To let dpvs work properly with multi-cores, rte_flow items must support "ipv4, ipv6, tcp, udp" four items, and rte_flow actions must support "drop, queue" at least. +> Notes: +> 1. Please check this link for NICs supported by DPDK: http://dpdk.org/doc/nics. +> 2. `Flow Control` ([rte_flow](http://dpdk.org/doc/guides/nics/overview.html#id1)) is required for `FNAT` and `SNAT` mode when DPVS running on multi-cores unless `conn redirect` is enabled. The minimum requirements to ensure DPVS works with multi-core properly is that `rte_flow` must support "ipv4, ipv6, tcp, udp" four items, and "drop, queue" two actions. +> 3. DPVS doesn't confine itself to the this test environments. In fact, DPVS is an user-space application which relies very little on operating system, kernel versions, compilers, and other platform discrepancies. As far as is known, DPVS has been verified at least in the following environments. +> * Centos 7.2, 7.6, 7.9 +> * Anolis 8.6, 8.8, 8.9 +> * GCC 4.8, 8.5 +> * Kernel: 3.10.0, 4.18.0, 5.10.134 +> * NIC: Intel IXGBE, NVIDIA MLX5 ## Clone DPVS @@ -65,40 +72,40 @@ $ cd dpvs Well, let's start from DPDK then. -## DPDK setup. +## DPDK setup -Currently, `dpdk-stable-20.11.1` is recommended for `DPVS`, and we will not support dpdk version earlier than dpdk-20.11 any more. If you are still using earlier dpdk versions, such as `dpdk-stable-17.11.2`, `dpdk-stable-17.11.6` and `dpdk-stable-18.11.2`, please use earlier dpvs releases, such as [v1.8.10](https://github.com/iqiyi/dpvs/releases/tag/v1.8.10). +Currently, `dpdk-stable-20.11.10` is recommended for `DPVS`, and we will not support dpdk version earlier than dpdk-20.11 any more. If you are still using earlier dpdk versions, such as `dpdk-stable-17.11.6` and `dpdk-stable-18.11.2`, please use earlier DPVS releases, such as [v1.8.12](https://github.com/iqiyi/dpvs/releases/tag/v1.8.12). > Notes: You can skip this section if experienced with DPDK, and refer the [link](http://dpdk.org/doc/guides/linux_gsg/index.html) for details. ```bash -$ wget https://fast.dpdk.org/rel/dpdk-20.11.1.tar.xz # download from dpdk.org if link failed. -$ tar xf dpdk-20.11.1.tar.xz +$ wget https://fast.dpdk.org/rel/dpdk-20.11.10.tar.xz # download from dpdk.org if link failed. +$ tar xf dpdk-20.11.10.tar.xz ``` ### DPDK patchs There are some patches for DPDK to support extra features needed by DPVS. Apply them if needed. For example, there's a patch for DPDK `kni` driver for hardware multicast, apply it if you are to launch `ospfd` on `kni` device. -> Notes: Assuming we are in DPVS root directory and dpdk-stable-20.11.1 is under it, please note it's not mandatory, just for convenience. +> Notes: It's assumed we are in DPVS root directory where you have installed dpdk-stable-20.11.10 source codes. Please note it's not mandatory, just for convenience. ``` $ cd -$ cp patch/dpdk-stable-20.11.1/*.patch dpdk-stable-20.11.1/ -$ cd dpdk-stable-20.11.1/ +$ cp patch/dpdk-stable-20.11.10/*.patch dpdk-stable-20.11.10/ +$ cd dpdk-stable-20.11.10/ $ patch -p1 < 0001-kni-use-netlink-event-for-multicast-driver-part.patch $ patch -p1 < 0002-pdump-change-dpdk-pdump-tool-for-dpvs.patch $ ... ``` > Tips: It's advised to patch all if your are not sure about what they are meant for. - + ### DPDK build and install -Use meson-ninja to build DPDK libraries, and export environment variable `PKG_CONFIG_PATH` for DPDK app (DPVS). The `dpdk.mk` in DPVS checks the presence of libdpdk. +Use meson-ninja to build DPDK, and export environment variable `PKG_CONFIG_PATH` for DPDK application (DPVS). The sub-Makefile `src/dpdk.mk` in DPVS will check the presence of libdpdk. ```bash -$ cd dpdk-stable-20.11.1 +$ cd dpdk-stable-20.11.10 $ mkdir dpdklib # user desired install folder $ mkdir dpdkbuild # user desired build folder $ meson -Denable_kmods=true -Dprefix=dpdklib dpdkbuild @@ -115,31 +122,49 @@ Next is to set up DPDK hugepage. Our test environment is NUMA system. For single $ # for NUMA machine $ echo 8192 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages $ echo 8192 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages +``` +By default, hugetlbfs is mounted at `/dev/hugepages`, as shown below. +```bash +$ mount | grep hugetlbfs +hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime) +``` +If it's not your case, you should mount hugetlbfs by yourself. + +```bash $ mkdir /mnt/huge $ mount -t hugetlbfs nodev /mnt/huge ``` -Install kernel modules and bind NIC with `uio_pci_generic` driver. Quick start uses only one NIC, normally we use two for FNAT cluster, even four for bonding mode. For example, suppose the NIC we would use to run DPVS is eth0, in the meantime, we still keep another standalone NIC eth1 for debugging. +> Notes: +> 1. Hugepages of other size, such as 1GB-size hugepages, can also be used if your system supports. +> 2. It's recommended to reserve hugepage memory and isolate CPUs used by DPVS with linux kernel cmdline options in production environments, for example `isolcpus=1-9 default_hugepagesz=1G hugepagesz=1G hugepages=32`. + +Next, install kernel modules required by DPDK and DPVS. + +* DPDK driver kernel module: +Depending on your NIC and system, NIC may require binding a DPDK-compitable driver, such as `vfio-pci`, `igb_uio`, or `uio_pci_generic`. Refer to [DPDK doc](https://doc.dpdk.org/guides/linux_gsg/linux_drivers.html) for more details. In this test, we use the linux standard UIO kernel module `uio_pci_generic`. + +* KNI kernel module: +KNI kernel module `rte_kni.ko` is required by DPVS as the exception data path which processes packets not dealt with in DPVS to kernel stack. ```bash $ modprobe uio_pci_generic -$ cd dpdk-stable-20.11.1 +$ cd dpdk-stable-20.11.10 $ insmod dpdkbuild/kernel/linux/kni/rte_kni.ko carrier=on +$ # bind eth0 to uio_pci_generic (Be aware: Network on eth0 will get broken!) $ ./usertools/dpdk-devbind.py --status -$ ifconfig eth0 down # assuming eth0 is 0000:06:00.0 +$ ifconfig eth0 down # assuming eth0's pci-bus location is 0000:06:00.0 $ ./usertools/dpdk-devbind.py -b uio_pci_generic 0000:06:00.0 ``` - > Notes: -> 1. An alternative to the `uio_pci_generic` is `igb_uio`, which is moved to a separated repository [dpdk-kmods](http://git.dpdk.org/dpdk-kmods). -> 2. A kernel module parameter `carrier` is added to `rte_kni.ko` since [DPDK v18.11](https://elixir.bootlin.com/dpdk/v18.11/source/kernel/linux/kni/kni_misc.c), and the default value for it is "off". We need to load `rte_kni.ko` with the extra parameter `carrier=on` to make KNI devices work properly. - -`dpdk-devbind.py -u` can be used to unbind driver and switch it back to Linux driver like `ixgbe`. You can also use `lspci` or `ethtool -i eth0` to check the NIC PCI bus-id. Please refer to [DPDK site](http://www.dpdk.org) for more details. - -> Notes: PMD of Mellanox NIC is built on top of libibverbs using the Raw Ethernet Accelerated Verbs AP. It doesn't rely on UIO/VFIO driver. Thus, Mellanox NICs should not bind the `igb_uio` driver. Refer to [Mellanox DPDK](https://community.mellanox.com/s/article/mellanox-dpdk) for details. +> 1. The test in our Quick Start uses only one NIC. Bind as many NICs as required in your DPVS application to DPDK driver kernel module. For example, you should bind at least 2 NICs if you are testing DPVS with two-arm. +> 2. `dpdk-devbind.py -u` can be used to unbind driver and switch it back to Linux driver like `ixgbe`. Use `lspci` or `ethtool -i eth0` to check the NIC's PCI bus-id. Please refer to [DPDK Doc:Binding and Unbinding Network Ports to/from the Kernel Modules](https://doc.dpdk.org/guides/linux_gsg/linux_drivers.html#binding-and-unbinding-network-ports-to-from-the-kernel-modules) for more details. +> 3. NVIDIA/Mellanox NIC uses bifurcated driver which doesn't rely on UIO/VFIO driver, so not bind any DPDK driver kernel module, but [NVIDIA MLNX_OFED/EN](https://network.nvidia.com/products/infiniband-drivers/linux/mlnx_ofed/) is required. Refer to [Mellanox DPDK](https://enterprise-support.nvidia.com/s/article/mellanox-dpdk) for its PMD and [Compilation Prerequisites](https://doc.dpdk.org/guides/platform/mlx5.html#linux-prerequisites) for OFED installation. +> 4. A kernel module parameter `carrier` has been added to `rte_kni.ko` since [DPDK v18.11](https://elixir.bootlin.com/dpdk/v18.11/source/kernel/linux/kni/kni_misc.c), and the default value for it is "off". We need to load `rte_kni.ko` with extra parameter `carrier=on` to make KNI devices work properly. +> 5. Multiple DPVS instances can run on a single server if there are enough NICs or VFs within one NIC. Refer to [tutorial:Multiple Instances](https://github.com/iqiyi/dpvs/blob/devel/doc/tutorial.md#multi-instance) for details. ## Build DPVS @@ -153,19 +178,21 @@ $ make # or "make -j" to speed up $ make install ``` > Notes: -> 1. Build dependencies may be needed, such as `pkg-config`(version 0.29.2+),`automake`, `libnl3`, `libnl-genl-3.0`, `openssl`, `popt` and `numactl`. You can install the missing dependencies by using the package manager of the system, e.g., `yum install popt-devel automake` (CentOS) or `apt install libpopt-dev autoconfig` (Ubuntu). -> 2. Early `pkg-config` versions (v0.29.2 before) may cause dpvs build failure. If so, please upgrade this tool. +> 1. Build dependencies may be needed, such as `pkg-config`(version 0.29.2+, `automake`, `libnl3`, `libnl-genl-3.0`, `openssl`, `popt` and `numactl`. You can install the missing dependencies with package manager of your system, e.g., `yum install popt-devel automake` (CentOS) or `apt install libpopt-dev autoconfig` (Ubuntu). +> 2. Early `pkg-config` versions (v0.29.2 before) may cause dpvs build failure. If so, please upgrade this tool. Specially, you may upgrade the `pkg-config` on Centos7 to meet the version requirement. +> 3. If you want to compile `dpvs-agent` and `healthcheck`, enable `CONFIG_DPVS_AGENT` in config.mk, and install Golang build environments(Refer to [go.mod](tools/dpvs-agent/go.mod) file for required Golang version). -Output files are installed to `dpvs/bin`. +Output binary files are installed to `dpvs/bin`. ```bash $ ls bin/ -dpip dpvs ipvsadm keepalived +dpip dpvs dpvs-agent healthcheck ipvsadm keepalived ``` * `dpvs` is the main program. -* `dpip` is the tool to set IP address, route, vlan, neigh, etc. +* `dpip` is the tool to manage IP address, route, vlan, neigh, etc. * `ipvsadm` and `keepalived` come from LVS, both are modified. +* `dpvs-agent` and `healthcheck` are alternatives to `keepalived` powered with HTTP API developed with Golang. ## Launch DPVS @@ -180,7 +207,13 @@ and start DPVS, ```bash $ cd /bin $ ./dpvs & + +$ # alternatively and strongly advised, start DPVS with NIC and CPU explicitly specified: +$ ./dpvs -- -a 0000:06:00.0 -l 1-9 ``` +> Notes: +> 1. Run `./dpvs --help` for DPVS supported command line options, and `./dpvs -- --help` for common DPDK EAL command line options. +> 2. The default `dpvs.conf` require 9 CPUs(1 master worker, 8 slave workers), modify it if not so many available CPUs in your system. Check if it's get started ? @@ -198,9 +231,9 @@ If you see this message. Well done, `DPVS` is working with NIC `dpdk0`! EAL: Error - exiting with code: 1 Cause: ports in DPDK RTE (2) != ports in dpvs.conf(1) ``` ->It means the NIC count of DPVS does not match `/etc/dpvs.conf`. Please use `dpdk-devbind` to adjust the NIC number or modify `dpvs.conf`. We'll improve this part to make DPVS more "clever" to avoid modify config file when NIC count does not match. +>It means the number of NIC recognized by DPVS mismatched `/etc/dpvs.conf`. Please either modify NIC number in `dpvs.conf` or specify NICs with EAL option `-a` explicitly. -What config items does `dpvs.conf` support? How to configure them? Well, `DPVS` maintains a config item file `conf/dpvs.conf.items` which lists all supported config entries and corresponding feasible values. Besides, some config sample files maintained as `./conf/dpvs.*.sample` show the configurations of dpvs in some specified cases. +What config items does `dpvs.conf` support? How to configure them? Well, `DPVS` maintains a config item file `conf/dpvs.conf.items` which lists all supported config entries, default values, and feasible value ranges. Besides, some sample config files maintained in `./conf/dpvs.*.sample` gives practical configurations of DPVS in corresponding circumstances. ## Test Full-NAT (FNAT) Load Balancer @@ -218,7 +251,7 @@ RS=192.168.100.2 ./dpip addr add ${VIP}/24 dev dpdk0 ./ipvsadm -A -t ${VIP}:80 -s rr -./ipvsadm -a -t ${VIP}:80 -r ${RS} -b +./ipvsadm -a -t ${VIP}:80 -r ${RS}:80 -b ./ipvsadm --add-laddr -z ${LIP} -t ${VIP}:80 -F dpdk0 $ @@ -234,22 +267,30 @@ Your ip:port : 192.168.100.3:56890 ## Tutorial Docs -More configure examples can be found in the [Tutorial Document](./doc/tutorial.md). Including, +More examples can be found in the [Tutorial Document](./doc/tutorial.md). Including, * WAN-to-LAN `FNAT` reverse proxy. * Direct Route (`DR`) mode setup. * Master/Backup model (`keepalived`) setup. * OSPF/ECMP cluster model setup. * `SNAT` mode for Internet access from internal network. -* Virtual Devices (`Bonding`, `VLAN`, `kni`, `ipip`/`GRE`). +* Virtual Devices (`Bonding`, `VLAN`, `kni`, `ipip`/`GRE` tunnel). * `UOA` module to get real UDP client IP/port in `FNAT`. * ... and more ... We also listed some frequently asked questions in the [FAQ Document](./doc/faq.md). It may help when you run into problems with DPVS. +Browse the [doc](./doc) directory for other documentations, including: +* [IPset](./doc/IPset.md) +* [Traffic Control (TC)](./doc/tc.md) +* [Performance tune](./doc/Worker-Performance-Tuning.md) +* [Backend healthcheck without keepalived](./doc/dest-check.md) +* [Client address conservation in Fullnat](./doc/client-address-conservation-in-fullnat.md) +* [Advices to build and run DPVS in container](./doc/containerized/README.md) + # Performance Test -Our test shows the forwarding speed (pps) of DPVS is several times than LVS and as good as Google's [Maglev](https://research.google.com/pubs/pub44824.html). +Our test shows the forwarding speed (PPS/packets per second) of DPVS is several times than LVS and as good as Google's [Maglev](https://research.google.com/pubs/pub44824.html). ![performance](./pic/performance.png) diff --git a/conf/dpvs.bond.conf.sample b/conf/dpvs.bond.conf.sample index d8cc1e5e8..d35d10328 100644 --- a/conf/dpvs.bond.conf.sample +++ b/conf/dpvs.bond.conf.sample @@ -17,6 +17,7 @@ global_defs { ! log_async_mode off ! kni on ! pdump off + lldp on } ! netif config diff --git a/conf/dpvs.conf.items b/conf/dpvs.conf.items index 5b5f9b35e..80f2e123a 100644 --- a/conf/dpvs.conf.items +++ b/conf/dpvs.conf.items @@ -19,6 +19,7 @@ global_defs { log_async_pool_size 16383 <16383, 1023-unlimited> pdump off kni on + lldp on } ! netif config @@ -193,6 +194,9 @@ ipv4_defs { ipv6_defs { disable off forwarding off + addr_gen_mode eui64 + stable_secret "" <128-bit hexadecimal string, used in stable-privacy mode > + route6 { method "hlist" <"hlist"/"lpm"> recycle_time 10 <10, 1-36000> diff --git a/conf/dpvs.conf.sample b/conf/dpvs.conf.sample index 14b0846d5..002aab56f 100644 --- a/conf/dpvs.conf.sample +++ b/conf/dpvs.conf.sample @@ -17,6 +17,7 @@ global_defs { ! log_async_mode on ! kni on ! pdump off + lldp on } ! netif config diff --git a/conf/dpvs.conf.single-bond.sample b/conf/dpvs.conf.single-bond.sample index 3fdfbfd33..b0c1c375a 100644 --- a/conf/dpvs.conf.single-bond.sample +++ b/conf/dpvs.conf.single-bond.sample @@ -16,6 +16,7 @@ global_defs { ! log_file /var/log/dpvs.log ! log_async_mode on ! kni on + lldp on } ! netif config diff --git a/conf/dpvs.conf.single-nic.sample b/conf/dpvs.conf.single-nic.sample index 3717ed07b..bb9ce994e 100644 --- a/conf/dpvs.conf.single-nic.sample +++ b/conf/dpvs.conf.single-nic.sample @@ -16,6 +16,7 @@ global_defs { ! log_file /var/log/dpvs.log ! log_async_mode on ! kni on + lldp on } ! netif config diff --git a/doc/IPset.md b/doc/IPset.md index 06a36b5e2..e5f4645db 100644 --- a/doc/IPset.md +++ b/doc/IPset.md @@ -459,7 +459,7 @@ bin/dpip: invalid parameter The hash:ip,port,net set type uses a hash table to store IP address, port number and IP network address triples. Both IPv4 and IPv6 address are supported. The IP address of the IP and net should be of the same family. When adding/deleting entries, ranges are allowed but is transformed to specific host IP and port entries when stored into hash table for the "ip" and "port" segments. IPv4 supports both IP range and IP CIDR, while IPv6 supports IP CIDR only. Network address with zero prefix size is not supported, and is interpreted as host prefix size, i.e., 32 for IPv4 and 128 for IPv6. Option "nomatch" can be used to set exceptions to the set when add/deleting entries. If a test is matched against with a "nomatch" entry, then the result would end with false. The port number is interpreted together with a protocol. Supported protocols include TCP, UDP, ICMP, and ICMPv6, any other protocols are interpreted as unspec type with a protocol number of zero. ```bash -# ./bin/dpip ipset create bar hash:ip,port,net +# ./bin/dpip ipset -6 create bar hash:ip,port,net # ./bin/dpip ipset add bar 2001::1,8080-8082,2002::/64 # ./bin/dpip ipset add bar 2001::1,8080-8082,2002::aaaa:bbbb:ccc0:0/108 nomatch # ./bin/dpip ipset -v list bar diff --git a/include/conf/blklst.h b/include/conf/blklst.h index 2b2c9a044..84d7be271 100644 --- a/include/conf/blklst.h +++ b/include/conf/blklst.h @@ -24,6 +24,7 @@ #include "inet.h" #include "conf/sockopts.h" +#include "conf/ipset.h" struct dp_vs_blklst_entry { union inet_addr addr; @@ -31,15 +32,14 @@ struct dp_vs_blklst_entry { typedef struct dp_vs_blklst_conf { /* identify service */ - union inet_addr blklst; union inet_addr vaddr; - int af; - uint32_t fwmark; uint16_t vport; uint8_t proto; - uint8_t padding; + uint8_t af; - /* for set */ + /* subject and ipset are mutual exclusive */ + union inet_addr subject; + char ipset[IPSET_MAXNAMELEN]; } dpvs_blklst_t; struct dp_vs_blklst_conf_array { diff --git a/include/conf/common.h b/include/conf/common.h index 7472ad8f1..14a048374 100644 --- a/include/conf/common.h +++ b/include/conf/common.h @@ -22,6 +22,7 @@ #include #include #include +#include #include typedef uint32_t sockoptid_t; @@ -142,6 +143,7 @@ int linux_get_link_status(const char *ifname, int *if_flags, char *if_flags_str, int linux_set_if_mac(const char *ifname, const unsigned char mac[ETH_ALEN]); int linux_hw_mc_add(const char *ifname, const uint8_t hwma[ETH_ALEN]); int linux_hw_mc_del(const char *ifname, const uint8_t hwma[ETH_ALEN]); +int linux_ifname2index(const char *ifname); /* read "n" bytes from a descriptor */ ssize_t readn(int fd, void *vptr, size_t n); @@ -166,4 +168,40 @@ static inline char *strlwr(char *str) { return str; } +/* convert hexadecimal string to binary sequence, return the converted binary length + * note: buflen should be half in size of len at least */ +int hexstr2binary(const char *hexstr, size_t len, uint8_t *buf, size_t buflen); + +/* convert binary sequence to hexadecimal string, return the converted string length + * note: buflen should be twice in size of len at least */ +int binary2hexstr(const uint8_t *hex, size_t len, char *buf, size_t buflen); + +/* convert binary sequence to printable or hexadecimal string, return the converted string length + * note: buflen should be triple in size of len in the worst case */ +int binary2print(const uint8_t *hex, size_t len, char *buf, size_t buflen); + +/* get prefix from network mask */ +int mask2prefix(const struct sockaddr *addr); + +/* get host addresses and corresponding interfaces + * + * Loopback addresses, ipv6 link local addresses, and addresses on linked-down + * or not-running interface are ignored. If multiple addresses matched, return + * the address of the least prefix length. + * + * Params: + * @ifname: preferred interface where to get host address, can be NULL + * @result4: store ipv4 address found, can be NULL + * @result6: store ipv6 address found, can be NULL + * @ifname4: interface name of ipv4 address, can be NULL + * @ifname6: interface name of ipv6 address, can be NULL + * Return: + * 1: only ipv4 address found + * 2: only ipv6 address found + * 3: both ipv4 and ipv6 address found + * dpvs error code: error occurred + * */ +int get_host_addr(const char *ifname, struct sockaddr_storage *result4, + struct sockaddr_storage *result6, char *ifname4, char *ifname6); + #endif /* __DPVS_COMMON_H__ */ diff --git a/include/conf/inet.h b/include/conf/inet.h index 1c3449781..6ae6c7e9c 100644 --- a/include/conf/inet.h +++ b/include/conf/inet.h @@ -89,6 +89,7 @@ static inline const char *inet_proto_name(uint8_t proto) const static char *proto_names[256] = { [IPPROTO_TCP] = "TCP", [IPPROTO_UDP] = "UDP", + [IPPROTO_SCTP] = "SCTP", [IPPROTO_ICMP] = "ICMP", [IPPROTO_ICMPV6] = "ICMPV6", }; diff --git a/include/conf/inetaddr.h b/include/conf/inetaddr.h index f97994f86..252e6f3ee 100644 --- a/include/conf/inetaddr.h +++ b/include/conf/inetaddr.h @@ -34,6 +34,7 @@ enum { /* leverage IFA_F_XXX in linux/if_addr.h*/ #define IFA_F_SAPOOL 0x10000 /* if address with sockaddr pool */ +#define IFA_F_LINKLOCAL 0x20000 /* ipv6 link-local address */ /* ifa command flags */ #define IFA_F_OPS_VERBOSE 0x0001 @@ -100,4 +101,17 @@ struct inet_addr_front { }; #endif /* CONFIG_DPVS_AGENT */ +struct inet_maddr_entry { + char ifname[IFNAMSIZ]; + union inet_addr maddr; + int af; + uint32_t flags; + uint32_t refcnt; +} __attribute__((__packed__)); + +struct inet_maddr_array { + int nmaddr; + struct inet_maddr_entry maddrs[0]; +} __attribute__((__packed__)); + #endif /* __DPVS_INETADDR_CONF_H__ */ diff --git a/include/conf/ipset.h b/include/conf/ipset.h index 7a080a226..1e59cc66c 100644 --- a/include/conf/ipset.h +++ b/include/conf/ipset.h @@ -32,7 +32,7 @@ #define IPSET_F_FORCE 0x0001 enum ipset_op { - IPSET_OP_ADD, + IPSET_OP_ADD = 1, IPSET_OP_DEL, IPSET_OP_TEST, IPSET_OP_CREATE, @@ -43,34 +43,36 @@ enum ipset_op { }; struct ipset_option { - int family; union { struct { - bool comment; - int hashsize; - int maxelem; - } create; + int32_t hashsize; + uint32_t maxelem; + uint8_t comment; + } __attribute__((__packed__)) create; struct { - bool nomatch; - } add; + char padding[8]; + uint8_t nomatch; + } __attribute__((__packed__)) add; }; -}; + uint8_t family; +} __attribute__((__packed__)); struct ipset_param { char type[IPSET_MAXNAMELEN]; char name[IPSET_MAXNAMELEN]; char comment[IPSET_MAXCOMLEN]; - int opcode; - struct ipset_option option; + uint16_t opcode; uint16_t flag; + struct ipset_option option; uint8_t proto; uint8_t cidr; struct inet_addr_range range; /* port in host byteorder */ - uint8_t mac[6]; char iface[IFNAMSIZ]; + uint8_t mac[6]; /* for type with 2 nets */ + uint8_t padding; uint8_t cidr2; struct inet_addr_range range2; //uint8_t mac[2]; @@ -83,43 +85,48 @@ struct ipset_member { uint8_t cidr; uint8_t proto; uint16_t port; - uint8_t mac[6]; char iface[IFNAMSIZ]; - bool nomatch; + uint8_t mac[6]; + uint8_t nomatch; /* second net */ - union inet_addr addr2; uint8_t cidr2; uint16_t port2; + uint8_t padding[2]; + union inet_addr addr2; }; struct ipset_info { char name[IPSET_MAXNAMELEN]; char type[IPSET_MAXNAMELEN]; - bool comment; + uint8_t comment; + + uint8_t af; + uint8_t padding[2]; union { struct ipset_bitmap_header { - struct inet_addr_range range; uint8_t cidr; + uint8_t padding[3]; + struct inet_addr_range range; } bitmap; struct ipset_hash_header { - int hashsize; - int maxelem; + uint8_t padding[4]; // aligned for dpvs-agent + int32_t hashsize; + uint32_t maxelem; } hash; }; - int af; - size_t size; - int entries; - int references; + uint32_t size; + uint32_t entries; + uint32_t references; void *members; }; struct ipset_info_array { - int nipset; - struct ipset_info infos[0]; + uint32_t nipset; + struct ipset_info infos[0]; } __attribute__((__packed__)); #endif /* __DPVS_IPSET_CONF_H__ */ diff --git a/include/conf/lldp.h b/include/conf/lldp.h new file mode 100644 index 000000000..24f8b7831 --- /dev/null +++ b/include/conf/lldp.h @@ -0,0 +1,41 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#ifndef __DPVS_LLDP_CONF_H__ +#define __DPVS_LLDP_CONF_H__ + +#include +#include "conf/sockopts.h" + +#define LLDP_MESSAGE_LEN 4096 + +#define DPVS_LLDP_NODE_LOCAL 0 +#define DPVS_LLDP_NODE_NEIGH 1 +#define DPVS_LLDP_NODE_MAX 2 + + +struct lldp_param { + uint16_t node; /* DPVS_LLDP_NODE_xxx */ + char ifname[IFNAMSIZ]; +}; + +struct lldp_message { + struct lldp_param param; + char message[LLDP_MESSAGE_LEN]; +}; + +#endif /* __DPVS_LLDP_CONF_H__ */ diff --git a/include/conf/match.h b/include/conf/match.h index 30a9d1e17..e0eac905a 100644 --- a/include/conf/match.h +++ b/include/conf/match.h @@ -93,6 +93,8 @@ static inline int parse_match(const char *pattern, uint8_t *proto, *proto = IPPROTO_TCP; } else if (strcmp(tok, "udp") == 0) { *proto = IPPROTO_UDP; + } else if (strcmp(tok, "sctp") == 0) { + *proto = IPPROTO_SCTP; } else if (strcmp(tok, "icmp") == 0) { *proto = IPPROTO_ICMP; } else if (strcmp(tok, "icmp6") == 0) { diff --git a/include/conf/netif.h b/include/conf/netif.h index 7ef7def4d..da11e402a 100644 --- a/include/conf/netif.h +++ b/include/conf/netif.h @@ -111,6 +111,7 @@ typedef struct netif_nic_basic_get uint16_t ol_tx_ip_csum:1; uint16_t ol_tx_tcp_csum:1; uint16_t ol_tx_udp_csum:1; + uint16_t lldp:1; } netif_nic_basic_get_t; /* nic statistics specified by port_id */ @@ -247,6 +248,8 @@ typedef struct netif_nic_set { uint16_t tc_egress_off:1; uint16_t tc_ingress_on:1; uint16_t tc_ingress_off:1; + uint16_t lldp_on:1; + uint16_t lldp_off:1; } netif_nic_set_t; typedef struct netif_bond_set { diff --git a/include/conf/netif_addr.h b/include/conf/netif_addr.h new file mode 100644 index 000000000..1a127e871 --- /dev/null +++ b/include/conf/netif_addr.h @@ -0,0 +1,37 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#ifndef __DPVS_NETIF_ADDR_CONF_H__ +#define __DPVS_NETIF_ADDR_CONF_H__ + +enum { + HW_ADDR_F_FROM_KNI = 1, // from linux kni device in local layer +}; + +struct netif_hw_addr_entry { + char addr[18]; + uint32_t refcnt; + uint16_t flags; + uint16_t sync_cnt; +} __attribute__((__packed__)); + +struct netif_hw_addr_array { + int count; + struct netif_hw_addr_entry entries[0]; +} __attribute__((__packed__)); + +#endif diff --git a/include/conf/service.h b/include/conf/service.h index d16164f3c..5da2f167a 100644 --- a/include/conf/service.h +++ b/include/conf/service.h @@ -42,6 +42,7 @@ #define IP_VS_SVC_F_SIP_HASH 0x0100 /* sip hash target */ #define IP_VS_SVC_F_QID_HASH 0x0200 /* quic cid hash target */ #define IP_VS_SVC_F_MATCH 0x0400 /* snat match */ +#define IP_VS_SVC_F_QUIC 0x0800 /* quic/h3 protocol */ #define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */ #define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */ @@ -52,8 +53,9 @@ #define DEST_HC_PASSIVE 0x01 #define DEST_HC_TCP 0x02 #define DEST_HC_UDP 0x04 -#define DEST_HC_PING 0x08 -#define DEST_HC_MASK_EXTERNAL 0x0e +#define DEST_HC_SCTP 0x08 +#define DEST_HC_PING 0x10 +#define DEST_HC_MASK_EXTERNAL 0x1e /* defaults for dest passive health check */ #define DEST_DOWN_NOTICE_DEFAULT 1 diff --git a/include/conf/sockopts.h b/include/conf/sockopts.h index 2e3cd7595..8539f9e9d 100644 --- a/include/conf/sockopts.h +++ b/include/conf/sockopts.h @@ -84,6 +84,7 @@ DPVSMSG(SOCKOPT_SET_IFADDR_SET) \ DPVSMSG(SOCKOPT_SET_IFADDR_FLUSH) \ DPVSMSG(SOCKOPT_GET_IFADDR_SHOW) \ + DPVSMSG(SOCKOPT_GET_IFMADDR_SHOW) \ \ DPVSMSG(SOCKOPT_NETIF_SET_LCORE) \ DPVSMSG(SOCKOPT_NETIF_SET_PORT) \ @@ -98,8 +99,12 @@ DPVSMSG(SOCKOPT_NETIF_GET_PORT_XSTATS) \ DPVSMSG(SOCKOPT_NETIF_GET_PORT_EXT_INFO) \ DPVSMSG(SOCKOPT_NETIF_GET_BOND_STATUS) \ + DPVSMSG(SOCKOPT_NETIF_GET_MADDR)\ DPVSMSG(SOCKOPT_NETIF_GET_MAX) \ \ + DPVSMSG(SOCKOPT_SET_LLDP_TODO) \ + DPVSMSG(SOCKOPT_GET_LLDP_SHOW) \ + \ DPVSMSG(SOCKOPT_SET_NEIGH_ADD) \ DPVSMSG(SOCKOPT_SET_NEIGH_DEL) \ DPVSMSG(SOCKOPT_GET_NEIGH_SHOW) \ diff --git a/include/conf/whtlst.h b/include/conf/whtlst.h index 8da9f4d57..4edc8880b 100644 --- a/include/conf/whtlst.h +++ b/include/conf/whtlst.h @@ -23,21 +23,22 @@ #define __DPVS_WHTLST_CONF_H__ #include "inet.h" #include "conf/sockopts.h" +#include "conf/ipset.h" + struct dp_vs_whtlst_entry { union inet_addr addr; }; typedef struct dp_vs_whtlst_conf { /* identify service */ - union inet_addr whtlst; union inet_addr vaddr; - int af; - uint32_t fwmark; uint16_t vport; uint8_t proto; - uint8_t padding; + uint8_t af; - /* for set */ + /* subject and ipset are mutual exclusive */ + union inet_addr subject; + char ipset[IPSET_MAXNAMELEN]; } dpvs_whtlst_t; struct dp_vs_whtlst_conf_array { diff --git a/include/ctrl.h b/include/ctrl.h index 888113f89..1df3d94f3 100644 --- a/include/ctrl.h +++ b/include/ctrl.h @@ -201,6 +201,7 @@ int msg_dump(const struct dpvs_msg *msg, char *buf, int len); #define MSG_TYPE_IPV6_STATS 16 #define MSG_TYPE_ROUTE6 17 #define MSG_TYPE_NEIGH_GET 18 +#define MSG_TYPE_LLDP_RECV 19 #define MSG_TYPE_IFA_GET 22 #define MSG_TYPE_IFA_SET 23 #define MSG_TYPE_IFA_SYNC 24 @@ -213,6 +214,7 @@ int msg_dump(const struct dpvs_msg *msg, char *buf, int len); #define MSG_TYPE_IPSET_SET 40 #define MSG_TYPE_DEST_CHECK_NOTIFY_MASTER 41 #define MSG_TYPE_DEST_CHECK_NOTIFY_SLAVES 42 +#define MSG_TYPE_IFA_IDEVINIT 43 #define MSG_TYPE_IPVS_RANGE_START 100 /* for svc per_core, refer to service.h*/ diff --git a/include/inetaddr.h b/include/inetaddr.h index 9fb56269e..a96b62518 100644 --- a/include/inetaddr.h +++ b/include/inetaddr.h @@ -26,15 +26,24 @@ #include "dpdk.h" #include "list.h" + +enum { + IDEV_F_NO_IPV6 = 0x00000001, + IDEV_F_NO_ROUTE = 0x00000002, +}; + struct inet_device { struct netif_port *dev; struct list_head ifa_list[DPVS_MAX_LCORE]; /* inet_ifaddr list */ struct list_head ifm_list[DPVS_MAX_LCORE]; /* inet_ifmcaddr list*/ uint32_t ifa_cnt[DPVS_MAX_LCORE]; + uint32_t ifm_cnt[DPVS_MAX_LCORE]; rte_atomic32_t refcnt; /* not used yet */ + uint32_t flags; /* IDEV_F_XXX */ #define this_ifa_list ifa_list[rte_lcore_id()] #define this_ifm_list ifm_list[rte_lcore_id()] #define this_ifa_cnt ifa_cnt[rte_lcore_id()] +#define this_ifm_cnt ifm_cnt[rte_lcore_id()] }; /* @@ -46,7 +55,7 @@ struct inet_ifmcaddr { int af; union inet_addr addr; uint32_t flags; /* not used yet */ - rte_atomic32_t refcnt; + uint32_t refcnt; }; /* @@ -117,7 +126,12 @@ bool inet_chk_mcast_addr(int af, struct netif_port *dev, void inet_ifaddr_dad_failure(struct inet_ifaddr *ifa); -int idev_add_mcast_init(void *args); +struct inet_device *dev_get_idev(const struct netif_port *dev); + +void idev_put(struct inet_device *idev); + +int idev_addr_init(struct inet_device *idev); + int inet_addr_init(void); int inet_addr_term(void); diff --git a/include/ip_tunnel.h b/include/ip_tunnel.h index 499aefb69..9a9f366e6 100644 --- a/include/ip_tunnel.h +++ b/include/ip_tunnel.h @@ -98,6 +98,8 @@ int ip_tunnel_xmit(struct rte_mbuf *mbuf, struct netif_port *dev, int ip_tunnel_pull_header(struct rte_mbuf *mbuf, int hlen, __be16 in_proto); +int ip_tunnel_dev_init(struct netif_port *dev); +int ip_tunnel_set_mc_list(struct netif_port *dev); int ip_tunnel_get_link(struct netif_port *dev, struct rte_eth_link *link); int ip_tunnel_get_stats(struct netif_port *dev, struct rte_eth_stats *stats); int ip_tunnel_get_promisc(struct netif_port *dev, bool *promisc); diff --git a/include/ipset/ipset.h b/include/ipset/ipset.h index 68ded707a..6c1d3649a 100644 --- a/include/ipset/ipset.h +++ b/include/ipset/ipset.h @@ -30,7 +30,7 @@ #define IPSET #define RTE_LOGTYPE_IPSET RTE_LOGTYPE_USER1 -#define IPSET_ADT_MAX 3 +#define IPSET_ADT_MAX IPSET_OP_MAX struct ipset; diff --git a/include/ipv6.h b/include/ipv6.h index edb562d00..7a95736f0 100644 --- a/include/ipv6.h +++ b/include/ipv6.h @@ -33,6 +33,29 @@ #define IPV6 #define RTE_LOGTYPE_IPV6 RTE_LOGTYPE_USER1 +enum ip6_addr_gen_mode { + IP6_ADDR_GEN_MODE_EUI64 = 1, + IP6_ADDR_GEN_MODE_NONE, + IP6_ADDR_GEN_MODE_STABLE_PRIVACY, + IP6_ADDR_GEN_MODE_RANDOM, + IP6_ADDR_GFN_MODE_MAX = 64, +}; + +struct ipv6_stable_secret { + bool initialized; + struct in6_addr secret; +}; + +struct ipv6_config { + unsigned disable:1; + unsigned forwarding:1; + unsigned addr_gen_mode:6; + struct ipv6_stable_secret secret_stable; + struct ipv6_stable_secret secret_random; +}; + +const struct ipv6_config *ip6_config_get(void); + /* * helper functions */ diff --git a/include/ipvs/blklst.h b/include/ipvs/blklst.h index d3326be93..bb9b0cd06 100644 --- a/include/ipvs/blklst.h +++ b/include/ipvs/blklst.h @@ -18,20 +18,25 @@ #ifndef __DPVS_BLKLST_H__ #define __DPVS_BLKLST_H__ #include "conf/common.h" -#include "ipvs/service.h" #include "timer.h" +#include "ipvs/service.h" +#include "ipset/ipset.h" struct blklst_entry { struct list_head list; - int af; - uint8_t proto; - uint16_t vport; + union inet_addr vaddr; - union inet_addr blklst; + uint16_t vport; + uint8_t proto; + uint8_t af; + + union inet_addr subject; + struct ipset *set; + bool dst_match; /* internal use for ipset */ }; -struct blklst_entry *dp_vs_blklst_lookup(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst); +bool dp_vs_blklst_filtered(int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject, struct rte_mbuf *mbuf); void dp_vs_blklst_flush(struct dp_vs_service *svc); int dp_vs_blklst_init(void); diff --git a/include/ipvs/proto_sctp.h b/include/ipvs/proto_sctp.h new file mode 100644 index 000000000..232d9594d --- /dev/null +++ b/include/ipvs/proto_sctp.h @@ -0,0 +1,66 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#ifndef __DP_VS_PROTO_SCTP_H__ +#define __DP_VS_PROTO_SCTP_H__ + +#include +#include "sctp/sctp.h" + +enum dpvs_sctp_event_t { + DPVS_SCTP_DATA = 0, /* DATA, SACK, HEARTBEATs */ + DPVS_SCTP_INIT, + DPVS_SCTP_INIT_ACK, + DPVS_SCTP_COOKIE_ECHO, + DPVS_SCTP_COOKIE_ACK, + DPVS_SCTP_SHUTDOWN, + DPVS_SCTP_SHUTDOWN_ACK, + DPVS_SCTP_SHUTDOWN_COMPLETE, + DPVS_SCTP_ERROR, + DPVS_SCTP_ABORT, + DPVS_SCTP_EVENT_LAST +}; + +/* ip_vs_conn handling functions + * (from ip_vs_conn.c) + */ +enum { DPVS_DIR_INPUT = 0, + DPVS_DIR_OUTPUT, + DPVS_DIR_INPUT_ONLY, + DPVS_DIR_LAST, +}; + +/* SCTP State Values */ +enum dpvs_sctp_states { + DPVS_SCTP_S_NONE, + DPVS_SCTP_S_INIT1, + DPVS_SCTP_S_INIT, + DPVS_SCTP_S_COOKIE_SENT, + DPVS_SCTP_S_COOKIE_REPLIED, + DPVS_SCTP_S_COOKIE_WAIT, + DPVS_SCTP_S_COOKIE, + DPVS_SCTP_S_COOKIE_ECHOED, + DPVS_SCTP_S_ESTABLISHED, + DPVS_SCTP_S_SHUTDOWN_SENT, + DPVS_SCTP_S_SHUTDOWN_RECEIVED, + DPVS_SCTP_S_SHUTDOWN_ACK_SENT, + DPVS_SCTP_S_REJECTED, + DPVS_SCTP_S_CLOSED, + DPVS_SCTP_S_LAST +}; + +#endif diff --git a/include/ipvs/quic.h b/include/ipvs/quic.h new file mode 100644 index 000000000..767d9ebc1 --- /dev/null +++ b/include/ipvs/quic.h @@ -0,0 +1,181 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#ifndef __DPVS_QUIC_H__ +#define __DPVS_QUICH__ + +#include +#include "ipvs/service.h" +#include "conf/inet.h" + +/* + * In order to support QUIC connection migration, DPVS makes an agreement on + * the format of QUIC Connection ID(CID) into which backend address information + * is encoded. Specifically, backend server should generate its QUIC CIDs complying + * with the format defined as below. + * + * DPVS QUIC Connction ID Format { + * First Octet (8), + * L3 Address Length (3), + * L4 Address Flag (1), + * L3 Address (8...64), + * [ L4 Address (16) ] + * Nonce (32...140) + * } + * + * The notations in CID format definition follows the RFC 9000 name notational + * convention. For detailed explanation, please refer to + * https://datatracker.ietf.org/doc/html/rfc9000#name-notational-conventions. + * + * First Octet: 8 bits + * Allows for compatibility with ITEF QUIC-LB drafts. Not used in DPVS. + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-19 + * L3 Address Length: 3 bits + * The length of L3 Address in byte. Add 1 to the 3-bit value gets the actual + * length, which is in range 1...8. + * If the length less than legitimated length, i.e. 4 bytes for IPv4, 16 bytes + * for IPv6, the higher address bytes are truncated. + * L4 Address Flags: 1 bit + * Indicate whether L4 Address is included in this CID. + * 1 - L4 Address is included + * 0 - L4 Address is not included + * L3 Address: 8, 16, 24, 32, 40, 48, 56, 64 bits + * IPv4/IPv6 address with high bytes trimmed if necessary. + * Its length is specified by L3 Address Length. + * L4 Address: 16 bits, optional + * UDP port number. + * Nonce: 32 ~ 140 bits, and constrained by CID's max length of 160 bits + * This is server independent field, often filled with data generated randomly. + * A minimum length is 32 bits to satisfy the entropy requirement of QUIC protocol. + * + * DPVS QUIC CID adopts a variable-length code style. The server information takes + * a fixed 4-bit for address length, and a variable 8 ~ 48 bits for L3 and L4 addresses. + * DPVS may not take the whole L3/L4 Address into CID to reduce the CID length. For example, + * if all backend server are in private network cidr 192.168.0.0/16 listening on the same + * server port, then the use of lowest 16-bit L3 Address without L4 Address is appropriate. + * + * Note the server info in QUIC CID is not encrypted, and we don't plan to implement a quic + * server id allocator as required in IETF QUIC-LB drafts. This is just a simple, stateless + * and clear text encoding, which may subject to security vulnerability that can be exploited + * by an external observer to corelate CIDs of a QUIC connection easier. + */ + +#define DPVS_QUIC_DCID_BYTES_MIN 7 + +struct quic_server { + uint16_t wildcard; // enum value: 8, 16, 24, 32, 40, 48, 56, 64 + uint16_t port; // network endian + union inet_addr addr; +}; + +// Generate a Quic CID accepted by DPVS. The function demos an implementation +// for CID generator that may be used by Quic server applications on RS. +// +// For example, given +// cidlen: 10, l3len:2, l4len:2, +// svr_ip:192.168.111.222(0xC0A86FDE), svr_port:8029(0x1F5D) +// the function generator Quic CIDs like +// XX36 FDE1 F5DX XXXX XXXX +// where 'X' denotes a random hexadecimal. +// +// Params: +// af: l3 address family, valid values are (AF_INET, AF_INET6) +// cidlen: the expected cid total length in bytes, no less than DPVS_QUIC_DCID_BYTES_MIN +// l3len: length in bytes of l3 address to be encoded in cid, valid values are integers (1...8) +// l4len: length in bytes of l4 address to be encoded in cid, valid values are (0, 2) +// svr_ip: l3 address +// svr_port: l4 address +// cid: the result cid buffer, the buffer size must be no less than cidlen +static inline int quic_cid_generator(int af, int cidlen, + int l3len, int l4len, const union inet_addr *svr_ip, + uint16_t svr_port, char *cid) { + char rdbuf[20]; + int i, fd, ret, entropy, l4flag; + char *l3addr; + uint16_t l4addr; + + entropy = cidlen - l3len - l4len + 1; + l4flag = l4len > 0 ? 1 : 0; + if (AF_INET == af) + l3addr = (char *)svr_ip + (4 - l3len); + else + l3addr = (char *)svr_ip + (16 - l3len); + l4addr = svr_port; + + if (cidlen < DPVS_QUIC_DCID_BYTES_MIN || + l3len > 8 || l3len < 1 || + (l4len != 0 && l4len != 2) || + cidlen < l3len + l4len + 5) + return -1; + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + return -1; + ret = read(fd, rdbuf, entropy); + if (ret != entropy) + return -1; + + cid[0] = rdbuf[0]; + cid[1] = (((l3len - 1) & 0x7) << 5) + | ((l4flag & 0x1) << 4) + | ((*l3addr>> 4) & 0xf); + for (i = 0; i < l3len; i++) { + if (i == l3len - 1) + cid[2+i] = ((*l3addr & 0xf) << 4); + else + cid[2+i] = ((*l3addr & 0xf) << 4) | ((*(l3addr+1) >> 4) & 0xf); + l3addr++; + } + if (l4len > 0) { + cid[l3len+1] &= 0xf0; + cid[l3len+1] |= ((l4addr >> 12) & 0xf); + l4addr <<= 4; + cid[l3len+2] = (l4addr >> 8) & 0xff; + cid[l3len+3] = l4addr & 0xff; + } + cid[l3len+l4len+1] |= (rdbuf[1] & 0xf); + memcpy(&cid[l3len+l4len+2], &rdbuf[2], entropy - 3); + return 0; +} + +static inline void quic_dump_server(const struct quic_server *qsvr, + char *buf, int bufsize) { + int af; + char addrbuf[64] = { 0 }; + + buf[0] = '\0'; + af = qsvr->wildcard > 32 ? AF_INET6 : AF_INET; // an approximation, not accurate + if (NULL == inet_ntop(af, &qsvr->addr, addrbuf, sizeof(addrbuf))) + return; + if (AF_INET == af) + snprintf(buf, bufsize, "%s:%d", addrbuf, ntohs(qsvr->port)); + else + snprintf(buf, bufsize, "[%s]:%d", addrbuf, ntohs(qsvr->port)); +} + +// Parse backend server address information from mbuf into qsvr. +int quic_parse_server(const struct rte_mbuf *, + const struct dp_vs_iphdr *, + struct quic_server *); + +// Schedule a dpvs conn using the backend server specified by qsvr. +// Return NULL if the backend server doesn't exists in the svc's rs list. +struct dp_vs_conn* quic_schedule(const struct dp_vs_service *, + const struct quic_server *, + const struct dp_vs_iphdr *, + struct rte_mbuf *); + +#endif diff --git a/include/ipvs/service.h b/include/ipvs/service.h index a4cbff8e8..67c282e2b 100644 --- a/include/ipvs/service.h +++ b/include/ipvs/service.h @@ -45,6 +45,7 @@ #define DP_VS_SVC_F_SIP_HASH IP_VS_SVC_F_SIP_HASH #define DP_VS_SVC_F_QID_HASH IP_VS_SVC_F_QID_HASH #define DP_VS_SVC_F_MATCH IP_VS_SVC_F_MATCH +#define DP_VS_SVC_F_QUIC IP_VS_SVC_F_QUIC /* virtual service */ struct dp_vs_service { diff --git a/include/ipvs/stats.h b/include/ipvs/stats.h index 3e4f8db11..0332fd2d9 100644 --- a/include/ipvs/stats.h +++ b/include/ipvs/stats.h @@ -53,6 +53,7 @@ enum dp_vs_estats_type { SYNPROXY_CONN_REUSED_CLOSEWAIT, SYNPROXY_CONN_REUSED_LASTACK, DEFENCE_IP_FRAG_DROP, + DEFENCE_SCTP_DROP, DEFENCE_TCP_DROP, DEFENCE_UDP_DROP, FAST_XMIT_REJECT, diff --git a/include/ipvs/whtlst.h b/include/ipvs/whtlst.h index 2288d5f96..d447bc5cc 100644 --- a/include/ipvs/whtlst.h +++ b/include/ipvs/whtlst.h @@ -19,20 +19,23 @@ #define __DPVS_WHTLST_H__ #include "conf/common.h" #include "ipvs/service.h" +#include "ipset/ipset.h" struct whtlst_entry { struct list_head list; - int af; + union inet_addr vaddr; uint16_t vport; uint8_t proto; - union inet_addr whtlst; + uint8_t af; + + union inet_addr subject; + struct ipset *set; + bool dst_match; /* internal use of ipset */ }; -struct whtlst_entry *dp_vs_whtlst_lookup(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst); -bool dp_vs_whtlst_allow(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst); +bool dp_vs_whtlst_filtered(int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject, struct rte_mbuf *mbuf); void dp_vs_whtlst_flush(struct dp_vs_service *svc); int dp_vs_whtlst_init(void); diff --git a/include/linux_ipv6.h b/include/linux_ipv6.h index 05bc4ae3b..6d19b1b36 100644 --- a/include/linux_ipv6.h +++ b/include/linux_ipv6.h @@ -492,6 +492,19 @@ static inline int ipv6_saddr_preferred(int type) return 0; } +static inline bool ipv6_reserved_interfaceid(const struct in6_addr *addr) +{ + if ((addr->s6_addr32[2] | addr->s6_addr32[3]) == 0) + return true; + if (addr->s6_addr32[2] == htonl(0x02005eff) && + ((addr->s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) + return true; + if (addr->s6_addr32[2] == htonl(0xfdffffff) && + ((addr->s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) + return true; + return false; +} + #ifdef __DPVS__ /*functions below were edited from addrconf.c*/ diff --git a/include/lldp.h b/include/lldp.h new file mode 100644 index 000000000..e4f828a24 --- /dev/null +++ b/include/lldp.h @@ -0,0 +1,118 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ + +#ifndef __DPVS_LLDP_H__ +#define __DPVS_LLDP_H__ + +#define DPVS_LLDP_TYPE_MAX 128 + +/* IEEE 802.3AB Clause 9: TLV Types */ +enum { + LLDP_TYPE_END = 0, + LLDP_TYPE_CHASSIS_ID = 1, + LLDP_TYPE_PORT_ID = 2, + LLDP_TYPE_TTL = 3, + LLDP_TYPE_PORT_DESC = 4, + LLDP_TYPE_SYS_NAME = 5, + LLDP_TYPE_SYS_DESC = 6, + LLDP_TYPE_SYS_CAP = 7, + LLDP_TYPE_MNG_ADDR = 8, + LLDP_TYPE_ORG = 127, +}; +#define LLDP_TYPE_VALID(t) (((t) >= 0) && ((t) < DPVS_LLDP_TYPE_MAX)) + +/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ +enum { + LLDP_CHASSIS_ID_RESERVED = 0, + LLDP_CHASSIS_ID_CHASSIS_COMPONENT = 1, + LLDP_CHASSIS_ID_INTERFACE_ALIAS = 2, + LLDP_CHASSIS_ID_PORT_COMPONENT = 3, + LLDP_CHASSIS_ID_MAC_ADDRESS = 4, + LLDP_CHASSIS_ID_NETWORK_ADDRESS = 5, + LLDP_CHASSIS_ID_INTERFACE_NAME = 6, + LLDP_CHASSIS_ID_LOCALLY_ASSIGNED = 7, +}; +#define LLDP_CHASSIS_ID_VALID(t) (((t) > 0) && ((t) <= 7)) + +/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +enum { + LLDP_PORT_ID_RESERVED = 0, + LLDP_PORT_ID_INTERFACE_ALIAS = 1, + LLDP_PORT_ID_PORT_COMPONENT = 2, + LLDP_PORT_ID_MAC_ADDRESS = 3, + LLDP_PORT_ID_NETWORK_ADDRESS = 4, + LLDP_PORT_ID_INTERFACE_NAME = 5, + LLDP_PORT_ID_AGENT_CIRCUIT_ID = 6, + LLDP_PORT_ID_LOCALLY_ASSIGNED = 7, +}; +#define LLDP_PORT_ID_VALID(t) (((t) > 0) && ((t) <= 7)) + +/* + * IETF RFC 3232: + * http://www.iana.org/assignments/ianaaddressfamilynumbers-mib + */ +enum { + LLDP_ADDR_OTHER = 0, + LLDP_ADDR_IPV4 = 1, + LLDP_ADDR_IPV6 = 2, + LLDP_ADDR_NSAP = 3, + LLDP_ADDR_HDLC = 4, + LLDP_ADDR_BBN1822 = 5, + LLDP_ADDR_ALL802 = 6, + LLDP_ADDR_E163 = 7, + LLDP_ADDR_E164 = 8, + LLDP_ADDR_F69 = 9, + LLDP_ADDR_X121 = 10, + LLDP_ADDR_IPX = 11, + LLDP_ADDR_APPLETALK = 12, + LLDP_ADDR_DECNETIV = 13, + LLDP_ADDR_BANYANVINES = 14, + LLDP_ADDR_E164WITHNSAP = 15, + LLDP_ADDR_DNS = 16, + LLDP_ADDR_DISTINGUISHEDNAME = 17, + LLDP_ADDR_ASNUMBER = 18, + LLDP_ADDR_XTPOVERIPV4 = 19, + LLDP_ADDR_XTPOVERIPV6 = 20, + LLDP_ADDR_XTPNATIVEMODEXTP = 21, + LLDP_ADDR_FIBRECHANNELWWPN = 22, + LLDP_ADDR_FIBRECHANNELWWNN = 23, + LLDP_ADDR_GWID = 24, + LLDP_ADDR_AFI = 25, + LLDP_ADDR_RESERVED = 65535, +}; + +/* IEEE 802.1AB: Annex E, Table E.1: Organizationally Specific TLVs */ +enum { + LLDP_ORG_SPEC_PVID = 1, + LLDP_ORG_SPEC_PPVID = 2, + LLDP_ORG_SPEC_VLAN_NAME = 3, + LLDP_ORG_SPEC_PROTO_ID = 4, + LLDP_ORG_SPEC_VID_USAGE = 5, + LLDP_ORG_SPEC_MGMT_VID = 6, + LLDP_ORG_SPEC_LINK_AGGR = 7, +}; +#define LLDP_ORG_SPEC_VALID(t) (((t) > 0) && ((t) <= 7)) + +void dpvs_lldp_enable(void); +void dpvs_lldp_disable(void); +bool dpvs_lldp_is_enabled(void); + +int dpvs_lldp_init(void); +int dpvs_lldp_term(void); + +#endif diff --git a/include/mbuf.h b/include/mbuf.h index a8ccde221..7fb013a48 100644 --- a/include/mbuf.h +++ b/include/mbuf.h @@ -61,6 +61,7 @@ typedef void * mbuf_userdata_field_route_t; typedef enum { MBUF_FIELD_PROTO = 0, MBUF_FIELD_ROUTE, + MBUF_FIELD_ORIGIN_PORT, } mbuf_usedata_field_t; /** diff --git a/include/netif.h b/include/netif.h index 951f50bb7..274f4b3d4 100644 --- a/include/netif.h +++ b/include/netif.h @@ -22,6 +22,7 @@ #include "list.h" #include "dpdk.h" #include "inetaddr.h" +#include "netif_addr.h" #include "global_data.h" #include "timer.h" #include "tc/tc.h" @@ -47,6 +48,7 @@ enum { NETIF_PORT_FLAG_TC_EGRESS = (0x1<<10), NETIF_PORT_FLAG_TC_INGRESS = (0x1<<11), NETIF_PORT_FLAG_NO_ARP = (0x1<<12), + NETIF_PORT_FLAG_LLDP = (0x1<<13), }; /* max tx/rx queue number for each nic */ @@ -70,7 +72,7 @@ enum { #define NETIF_ALIGN 32 -#define NETIF_PORT_ID_INVALID 0xFF +#define NETIF_PORT_ID_INVALID NETIF_MAX_PORTS #define NETIF_PORT_ID_ALL NETIF_PORT_ID_INVALID #define NETIF_LCORE_ID_INVALID 0xFF @@ -205,31 +207,6 @@ struct netif_ops { int (*op_get_xstats)(struct netif_port *dev, netif_nic_xstats_get_t **xstats); }; -struct netif_hw_addr { - struct list_head list; - struct rte_ether_addr addr; - rte_atomic32_t refcnt; - /* - * - sync only once! - * - * for HA in upper dev, no matter how many times it's added, - * only sync once to lower (when sync_cnt is zero). - * - * and HA (upper)'s refcnt++, to mark lower dev own's it. - * - * - when to unsync? - * - * when del if HA (upper dev)'s refcnt is 1 and syn_cnt is not zero. - * means lower dev is the only owner and need be unsync. - */ - int sync_cnt; -}; - -struct netif_hw_addr_list { - struct list_head addrs; - int count; -}; - struct netif_port { char name[IFNAMSIZ]; /* device name */ portid_t id; /* device id */ @@ -286,25 +263,27 @@ int netif_unregister_pkt(struct pkt_type *pt); /**************************** port API ******************************/ struct netif_port* netif_port_get(portid_t id); +/* get netif by name, fail return NULL */ +struct netif_port* netif_port_get_by_name(const char *name); +bool is_physical_port(portid_t pid); +bool is_bond_port(portid_t pid); +void netif_physical_port_range(portid_t *start, portid_t *end); +void netif_bond_port_range(portid_t *start, portid_t *end); /* port_conf can be NULL for default port configure */ int netif_print_port_conf(const struct rte_eth_conf *port_conf, char *buf, int *len); int netif_print_port_queue_conf(portid_t pid, char *buf, int *len); -/* get netif by name, fail return NULL */ -struct netif_port* netif_port_get_by_name(const char *name); // function only for init or termination // int netif_port_conf_get(struct netif_port *port, struct rte_eth_conf *eth_conf); int netif_port_conf_set(struct netif_port *port, const struct rte_eth_conf *conf); int netif_port_start(struct netif_port *port); // start nic and wait until up int netif_port_stop(struct netif_port *port); // stop nic -int netif_set_mc_list(struct netif_port *port); -int __netif_set_mc_list(struct netif_port *port); int netif_get_queue(struct netif_port *port, lcoreid_t id, queueid_t *qid); int netif_get_link(struct netif_port *dev, struct rte_eth_link *link); int netif_get_promisc(struct netif_port *dev, bool *promisc); int netif_get_allmulticast(struct netif_port *dev, bool *allmulticast); int netif_get_stats(struct netif_port *dev, struct rte_eth_stats *stats); int netif_get_xstats(struct netif_port *dev, netif_nic_xstats_get_t **xstats); -struct netif_port *netif_alloc(size_t priv_size, const char *namefmt, +struct netif_port *netif_alloc(portid_t id, size_t priv_size, const char *namefmt, unsigned int nrxq, unsigned int ntxq, void (*setup)(struct netif_port *)); portid_t netif_port_count(void); diff --git a/include/netif_addr.h b/include/netif_addr.h index 1a6b97d71..e1d98aa8e 100644 --- a/include/netif_addr.h +++ b/include/netif_addr.h @@ -23,18 +23,41 @@ */ #ifndef __DPVS_NETIF_ADDR_H__ #define __DPVS_NETIF_ADDR_H__ -#include "netif.h" -int __netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr); -int __netif_mc_del(struct netif_port *dev, const struct rte_ether_addr *addr); +#include "conf/netif_addr.h" + +struct netif_hw_addr { + struct list_head list; + struct rte_ether_addr addr; + rte_atomic32_t refcnt; + uint16_t flags; + uint16_t sync_cnt; +}; + +struct netif_hw_addr_list { + struct list_head addrs; + int count; +}; + +struct netif_port; + +int __netif_hw_addr_add(struct netif_hw_addr_list *list, + const struct rte_ether_addr *addr, uint16_t flags); +int __netif_hw_addr_del(struct netif_hw_addr_list *list, + const struct rte_ether_addr *addr, uint16_t flags); + +int netif_set_mc_list(struct netif_port *dev); +int __netif_set_mc_list(struct netif_port *dev); + int netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr); int netif_mc_del(struct netif_port *dev, const struct rte_ether_addr *addr); void netif_mc_flush(struct netif_port *dev); void netif_mc_init(struct netif_port *dev); -int __netif_mc_dump(struct netif_port *dev, - struct rte_ether_addr *addrs, size_t *naddr); -int netif_mc_dump(struct netif_port *dev, - struct rte_ether_addr *addrs, size_t *naddr); + +int __netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, + struct rte_ether_addr *addrs, size_t *naddr); +int netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, + struct rte_ether_addr *addrs, size_t *naddr); int __netif_mc_print(struct netif_port *dev, char *buf, int *len, int *pnaddr); int netif_mc_print(struct netif_port *dev, @@ -45,10 +68,12 @@ int netif_mc_sync(struct netif_port *to, struct netif_port *from); int __netif_mc_unsync(struct netif_port *to, struct netif_port *from); int netif_mc_unsync(struct netif_port *to, struct netif_port *from); -int __netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from); -int netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from); -int __netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from); -int netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from); +int __netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); +int netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); +int __netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); +int netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); + +int netif_get_multicast_addrs(struct netif_port *dev, void **out, size_t *outlen); static inline int eth_addr_equal(const struct rte_ether_addr *addr1, const struct rte_ether_addr *addr2) @@ -69,4 +94,9 @@ static inline char *eth_addr_dump(const struct rte_ether_addr *ea, return buf; } +static bool inline hw_addr_from_kni(const struct netif_hw_addr *hwa) +{ + return !!(hwa->flags & HW_ADDR_F_FROM_KNI); +} + #endif /* __DPVS_NETIF_ADDR_H__ */ diff --git a/include/sctp/sctp.h b/include/sctp/sctp.h new file mode 100644 index 000000000..2d2d76100 --- /dev/null +++ b/include/sctp/sctp.h @@ -0,0 +1,664 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. + * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * a) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * b) Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * c) Neither the name of Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(__FreeBSD__) && !defined(__Userspace__) +#include +__FBSDID("$FreeBSD$"); +#endif + +#ifndef _NETINET_SCTP_H_ +#define _NETINET_SCTP_H_ + +#if defined(__APPLE__) || defined(__linux__) +#include +#endif +#include + +#if !defined(_WIN32) +#define SCTP_PACKED __attribute__((packed)) +#else +#pragma pack (push, 1) +#define SCTP_PACKED +#endif + +/* + * SCTP protocol - RFC4960. + */ +struct sctphdr { + uint16_t src_port; /* source port */ + uint16_t dest_port; /* destination port */ + uint32_t v_tag; /* verification tag of packet */ + uint32_t checksum; /* CRC32C checksum */ + /* chunks follow... */ +} SCTP_PACKED; + +/* + * SCTP Chunks + */ +struct sctp_chunkhdr { + uint8_t chunk_type; /* chunk type */ + uint8_t chunk_flags; /* chunk flags */ + uint16_t chunk_length; /* chunk length */ + /* optional params follow */ +} SCTP_PACKED; + +/* + * SCTP chunk parameters + */ +struct sctp_paramhdr { + uint16_t param_type; /* parameter type */ + uint16_t param_length; /* parameter length */ +} SCTP_PACKED; + +/* + * user socket options: socket API defined + */ +/* + * read-write options + */ +#define SCTP_RTOINFO 0x00000001 +#define SCTP_ASSOCINFO 0x00000002 +#define SCTP_INITMSG 0x00000003 +#define SCTP_NODELAY 0x00000004 +#define SCTP_AUTOCLOSE 0x00000005 +#define SCTP_SET_PEER_PRIMARY_ADDR 0x00000006 +#define SCTP_PRIMARY_ADDR 0x00000007 +#define SCTP_ADAPTATION_LAYER 0x00000008 +/* same as above */ +#define SCTP_ADAPTION_LAYER 0x00000008 +#define SCTP_DISABLE_FRAGMENTS 0x00000009 +#define SCTP_PEER_ADDR_PARAMS 0x0000000a +#define SCTP_DEFAULT_SEND_PARAM 0x0000000b +/* ancillary data/notification interest options */ +#define SCTP_EVENTS 0x0000000c /* deprecated */ +/* Without this applied we will give V4 and V6 addresses on a V6 socket */ +#define SCTP_I_WANT_MAPPED_V4_ADDR 0x0000000d +#define SCTP_MAXSEG 0x0000000e +#define SCTP_DELAYED_SACK 0x0000000f +#define SCTP_FRAGMENT_INTERLEAVE 0x00000010 +#define SCTP_PARTIAL_DELIVERY_POINT 0x00000011 +/* authentication support */ +#define SCTP_AUTH_CHUNK 0x00000012 +#define SCTP_AUTH_KEY 0x00000013 +#define SCTP_HMAC_IDENT 0x00000014 +#define SCTP_AUTH_ACTIVE_KEY 0x00000015 +#define SCTP_AUTH_DELETE_KEY 0x00000016 +#define SCTP_USE_EXT_RCVINFO 0x00000017 +#define SCTP_AUTO_ASCONF 0x00000018 /* rw */ +#define SCTP_MAXBURST 0x00000019 /* rw */ +#define SCTP_MAX_BURST 0x00000019 /* rw */ +/* assoc level context */ +#define SCTP_CONTEXT 0x0000001a /* rw */ +/* explicit EOR signalling */ +#define SCTP_EXPLICIT_EOR 0x0000001b +#define SCTP_REUSE_PORT 0x0000001c /* rw */ +#define SCTP_AUTH_DEACTIVATE_KEY 0x0000001d +#define SCTP_EVENT 0x0000001e +#define SCTP_RECVRCVINFO 0x0000001f +#define SCTP_RECVNXTINFO 0x00000020 +#define SCTP_DEFAULT_SNDINFO 0x00000021 +#define SCTP_DEFAULT_PRINFO 0x00000022 +#define SCTP_PEER_ADDR_THLDS 0x00000023 +#define SCTP_REMOTE_UDP_ENCAPS_PORT 0x00000024 +#define SCTP_ECN_SUPPORTED 0x00000025 +#define SCTP_PR_SUPPORTED 0x00000026 +#define SCTP_AUTH_SUPPORTED 0x00000027 +#define SCTP_ASCONF_SUPPORTED 0x00000028 +#define SCTP_RECONFIG_SUPPORTED 0x00000029 +#define SCTP_NRSACK_SUPPORTED 0x00000030 +#define SCTP_PKTDROP_SUPPORTED 0x00000031 +#define SCTP_MAX_CWND 0x00000032 + +/* + * read-only options + */ +#define SCTP_STATUS 0x00000100 +#define SCTP_GET_PEER_ADDR_INFO 0x00000101 +/* authentication support */ +#define SCTP_PEER_AUTH_CHUNKS 0x00000102 +#define SCTP_LOCAL_AUTH_CHUNKS 0x00000103 +#define SCTP_GET_ASSOC_NUMBER 0x00000104 /* ro */ +#define SCTP_GET_ASSOC_ID_LIST 0x00000105 /* ro */ +#define SCTP_TIMEOUTS 0x00000106 +#define SCTP_PR_STREAM_STATUS 0x00000107 +#define SCTP_PR_ASSOC_STATUS 0x00000108 + +/* + * user socket options: BSD implementation specific + */ +/* + * Blocking I/O is enabled on any TCP type socket by default. For the UDP + * model if this is turned on then the socket buffer is shared for send + * resources amongst all associations. The default for the UDP model is that + * is SS_NBIO is set. Which means all associations have a separate send + * limit BUT they will NOT ever BLOCK instead you will get an error back + * EAGAIN if you try to send too much. If you want the blocking semantics you + * set this option at the cost of sharing one socket send buffer size amongst + * all associations. Peeled off sockets turn this option off and block. But + * since both TCP and peeled off sockets have only one assoc per socket this + * is fine. It probably does NOT make sense to set this on SS_NBIO on a TCP + * model OR peeled off UDP model, but we do allow you to do so. You just use + * the normal syscall to toggle SS_NBIO the way you want. + * + * Blocking I/O is controlled by the SS_NBIO flag on the socket state so_state + * field. + */ + +#define SCTP_ENABLE_STREAM_RESET 0x00000900 /* struct sctp_assoc_value */ +#define SCTP_RESET_STREAMS 0x00000901 /* struct sctp_reset_streams */ +#define SCTP_RESET_ASSOC 0x00000902 /* sctp_assoc_t */ +#define SCTP_ADD_STREAMS 0x00000903 /* struct sctp_add_streams */ + +/* For enable stream reset */ +#define SCTP_ENABLE_RESET_STREAM_REQ 0x00000001 +#define SCTP_ENABLE_RESET_ASSOC_REQ 0x00000002 +#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x00000004 +#define SCTP_ENABLE_VALUE_MASK 0x00000007 +/* For reset streams */ +#define SCTP_STREAM_RESET_INCOMING 0x00000001 +#define SCTP_STREAM_RESET_OUTGOING 0x00000002 + +/* here on down are more implementation specific */ +#define SCTP_SET_DEBUG_LEVEL 0x00001005 +#define SCTP_CLR_STAT_LOG 0x00001007 +/* CMT ON/OFF socket option */ +#define SCTP_CMT_ON_OFF 0x00001200 +#define SCTP_CMT_USE_DAC 0x00001201 +/* JRS - Pluggable Congestion Control Socket option */ +#define SCTP_PLUGGABLE_CC 0x00001202 +/* RS - Pluggable Stream Scheduling Socket option */ +#define SCTP_PLUGGABLE_SS 0x00001203 +#define SCTP_SS_VALUE 0x00001204 +#define SCTP_CC_OPTION 0x00001205 /* Options for CC modules */ +/* For I-DATA */ +#define SCTP_INTERLEAVING_SUPPORTED 0x00001206 + +/* read only */ +#define SCTP_GET_SNDBUF_USE 0x00001101 +#define SCTP_GET_STAT_LOG 0x00001103 +#define SCTP_PCB_STATUS 0x00001104 +#define SCTP_GET_NONCE_VALUES 0x00001105 + +/* Special hook for dynamically setting primary for all assoc's, + * this is a write only option that requires root privilege. + */ +#define SCTP_SET_DYNAMIC_PRIMARY 0x00002001 + +/* VRF (virtual router feature) and multi-VRF support + * options. VRF's provide splits within a router + * that give the views of multiple routers. A + * standard host, without VRF support, is just + * a single VRF. If VRF's are supported then + * the transport must be VRF aware. This means + * that every socket call coming in must be directed + * within the endpoint to one of the VRF's it belongs + * to. The endpoint, before binding, may select + * the "default" VRF it is in by using a set socket + * option with SCTP_VRF_ID. This will also + * get propagated to the default VRF. Once the + * endpoint binds an address then it CANNOT add + * additional VRF's to become a Multi-VRF endpoint. + * + * Before BINDING additional VRF's can be added with + * the SCTP_ADD_VRF_ID call or deleted with + * SCTP_DEL_VRF_ID. + * + * Associations are ALWAYS contained inside a single + * VRF. They cannot reside in two (or more) VRF's. Incoming + * packets, assuming the router is VRF aware, can always + * tell us what VRF they arrived on. A host not supporting + * any VRF's will find that the packets always arrived on the + * single VRF that the host has. + * + */ + +#define SCTP_VRF_ID 0x00003001 +#define SCTP_ADD_VRF_ID 0x00003002 +#define SCTP_GET_VRF_IDS 0x00003003 +#define SCTP_GET_ASOC_VRF 0x00003004 +#define SCTP_DEL_VRF_ID 0x00003005 + +/* + * If you enable packet logging you can get + * a poor mans ethereal output in binary + * form. Note this is a compile option to + * the kernel, SCTP_PACKET_LOGGING, and + * without it in your kernel you + * will get a EOPNOTSUPP + */ +#define SCTP_GET_PACKET_LOG 0x00004001 + +/* + * hidden implementation specific options these are NOT user visible (should + * move out of sctp.h) + */ +/* sctp_bindx() flags as hidden socket options */ +#define SCTP_BINDX_ADD_ADDR 0x00008001 +#define SCTP_BINDX_REM_ADDR 0x00008002 +/* Hidden socket option that gets the addresses */ +#define SCTP_GET_PEER_ADDRESSES 0x00008003 +#define SCTP_GET_LOCAL_ADDRESSES 0x00008004 +/* return the total count in bytes needed to hold all local addresses bound */ +#define SCTP_GET_LOCAL_ADDR_SIZE 0x00008005 +/* Return the total count in bytes needed to hold the remote address */ +#define SCTP_GET_REMOTE_ADDR_SIZE 0x00008006 +/* hidden option for connectx */ +#define SCTP_CONNECT_X 0x00008007 +/* hidden option for connectx_delayed, part of sendx */ +#define SCTP_CONNECT_X_DELAYED 0x00008008 +#define SCTP_CONNECT_X_COMPLETE 0x00008009 +/* hidden socket option based sctp_peeloff */ +#define SCTP_PEELOFF 0x0000800a +/* the real worker for sctp_getaddrlen() */ +#define SCTP_GET_ADDR_LEN 0x0000800b +#if defined(__APPLE__) && !defined(__Userspace__) +/* temporary workaround for Apple listen() issue, no args used */ +#define SCTP_LISTEN_FIX 0x0000800c +#endif +#if defined(_WIN32) && !defined(__Userspace__) +/* workaround for Cygwin on Windows: returns the SOCKET handle */ +#define SCTP_GET_HANDLE 0x0000800d +#endif +/* Debug things that need to be purged */ +#define SCTP_SET_INITIAL_DBG_SEQ 0x00009f00 + +/* JRS - Supported congestion control modules for pluggable + * congestion control + */ +/* Standard TCP Congestion Control */ +#define SCTP_CC_RFC2581 0x00000000 +/* High Speed TCP Congestion Control (Floyd) */ +#define SCTP_CC_HSTCP 0x00000001 +/* HTCP Congestion Control */ +#define SCTP_CC_HTCP 0x00000002 +/* RTCC Congestion Control - RFC2581 plus */ +#define SCTP_CC_RTCC 0x00000003 + +#define SCTP_CC_OPT_RTCC_SETMODE 0x00002000 +#define SCTP_CC_OPT_USE_DCCC_ECN 0x00002001 +#define SCTP_CC_OPT_STEADY_STEP 0x00002002 + +#define SCTP_CMT_OFF 0 +#define SCTP_CMT_BASE 1 +#define SCTP_CMT_RPV1 2 +#define SCTP_CMT_RPV2 3 +#define SCTP_CMT_MPTCP 4 +#define SCTP_CMT_MAX SCTP_CMT_MPTCP + +/* RS - Supported stream scheduling modules for pluggable + * stream scheduling + */ +/* Default simple round-robin */ +#define SCTP_SS_DEFAULT 0x00000000 +/* Real round-robin */ +#define SCTP_SS_ROUND_ROBIN 0x00000001 +/* Real round-robin per packet */ +#define SCTP_SS_ROUND_ROBIN_PACKET 0x00000002 +/* Priority */ +#define SCTP_SS_PRIORITY 0x00000003 +/* Fair Bandwidth */ +#define SCTP_SS_FAIR_BANDWIDTH 0x00000004 +/* First-come, first-serve */ +#define SCTP_SS_FIRST_COME 0x00000005 + +/* fragment interleave constants + * setting must be one of these or + * EINVAL returned. + */ +#define SCTP_FRAG_LEVEL_0 0x00000000 +#define SCTP_FRAG_LEVEL_1 0x00000001 +#define SCTP_FRAG_LEVEL_2 0x00000002 + +/* + * user state values + */ +#define SCTP_CLOSED 0x0000 +#define SCTP_BOUND 0x1000 +#define SCTP_LISTEN 0x2000 +#define SCTP_COOKIE_WAIT 0x0002 +#define SCTP_COOKIE_ECHOED 0x0004 +#define SCTP_ESTABLISHED 0x0008 +#define SCTP_SHUTDOWN_SENT 0x0010 +#define SCTP_SHUTDOWN_RECEIVED 0x0020 +#define SCTP_SHUTDOWN_ACK_SENT 0x0040 +#define SCTP_SHUTDOWN_PENDING 0x0080 + +/* + * SCTP operational error codes (user visible) + */ +#define SCTP_CAUSE_NO_ERROR 0x0000 +#define SCTP_CAUSE_INVALID_STREAM 0x0001 +#define SCTP_CAUSE_MISSING_PARAM 0x0002 +#define SCTP_CAUSE_STALE_COOKIE 0x0003 +#define SCTP_CAUSE_OUT_OF_RESC 0x0004 +#define SCTP_CAUSE_UNRESOLVABLE_ADDR 0x0005 +#define SCTP_CAUSE_UNRECOG_CHUNK 0x0006 +#define SCTP_CAUSE_INVALID_PARAM 0x0007 +#define SCTP_CAUSE_UNRECOG_PARAM 0x0008 +#define SCTP_CAUSE_NO_USER_DATA 0x0009 +#define SCTP_CAUSE_COOKIE_IN_SHUTDOWN 0x000a +#define SCTP_CAUSE_RESTART_W_NEWADDR 0x000b +#define SCTP_CAUSE_USER_INITIATED_ABT 0x000c +#define SCTP_CAUSE_PROTOCOL_VIOLATION 0x000d + +/* Error causes from RFC5061 */ +#define SCTP_CAUSE_DELETING_LAST_ADDR 0x00a0 +#define SCTP_CAUSE_RESOURCE_SHORTAGE 0x00a1 +#define SCTP_CAUSE_DELETING_SRC_ADDR 0x00a2 +#define SCTP_CAUSE_ILLEGAL_ASCONF_ACK 0x00a3 +#define SCTP_CAUSE_REQUEST_REFUSED 0x00a4 + +/* Error causes from nat-draft */ +#define SCTP_CAUSE_NAT_COLLIDING_STATE 0x00b0 +#define SCTP_CAUSE_NAT_MISSING_STATE 0x00b1 + +/* Error causes from RFC4895 */ +#define SCTP_CAUSE_UNSUPPORTED_HMACID 0x0105 + +/* + * error cause parameters (user visible) + */ +struct sctp_gen_error_cause { + uint16_t code; + uint16_t length; + uint8_t info[]; +} SCTP_PACKED; + +struct sctp_error_cause { + uint16_t code; + uint16_t length; + /* optional cause-specific info may follow */ +} SCTP_PACKED; + +struct sctp_error_invalid_stream { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_INVALID_STREAM */ + uint16_t stream_id; /* stream id of the DATA in error */ + uint16_t reserved; +} SCTP_PACKED; + +struct sctp_error_missing_param { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_MISSING_PARAM */ + uint32_t num_missing_params; /* number of missing parameters */ + uint16_t type[]; +} SCTP_PACKED; + +struct sctp_error_stale_cookie { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_STALE_COOKIE */ + uint32_t stale_time; /* time in usec of staleness */ +} SCTP_PACKED; + +struct sctp_error_out_of_resource { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_OUT_OF_RESOURCES */ +} SCTP_PACKED; + +struct sctp_error_unresolv_addr { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_UNRESOLVABLE_ADDR */ +} SCTP_PACKED; + +struct sctp_error_unrecognized_chunk { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_UNRECOG_CHUNK */ + struct sctp_chunkhdr ch;/* header from chunk in error */ +} SCTP_PACKED; + +struct sctp_error_no_user_data { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_NO_USER_DATA */ + uint32_t tsn; /* TSN of the empty data chunk */ +} SCTP_PACKED; + +struct sctp_error_auth_invalid_hmac { + struct sctp_error_cause cause; /* code=SCTP_CAUSE_UNSUPPORTED_HMACID */ + uint16_t hmac_id; +} SCTP_PACKED; + +/* + * Main SCTP chunk types we place these here so natd and f/w's in user land + * can find them. + */ +/************0x00 series ***********/ +#define SCTP_DATA 0x00 +#define SCTP_INITIATION 0x01 +#define SCTP_INITIATION_ACK 0x02 +#define SCTP_SELECTIVE_ACK 0x03 +#define SCTP_HEARTBEAT_REQUEST 0x04 +#define SCTP_HEARTBEAT_ACK 0x05 +#define SCTP_ABORT_ASSOCIATION 0x06 +#define SCTP_SHUTDOWN 0x07 +#define SCTP_SHUTDOWN_ACK 0x08 +#define SCTP_OPERATION_ERROR 0x09 +#define SCTP_COOKIE_ECHO 0x0a +#define SCTP_COOKIE_ACK 0x0b +#define SCTP_ECN_ECHO 0x0c +#define SCTP_ECN_CWR 0x0d +#define SCTP_SHUTDOWN_COMPLETE 0x0e +/* RFC4895 */ +#define SCTP_AUTHENTICATION 0x0f +/* EY nr_sack chunk id*/ +#define SCTP_NR_SELECTIVE_ACK 0x10 +/************0x40 series ***********/ +#define SCTP_IDATA 0x40 +/************0x80 series ***********/ +/* RFC5061 */ +#define SCTP_ASCONF_ACK 0x80 +/* draft-ietf-stewart-pktdrpsctp */ +#define SCTP_PACKET_DROPPED 0x81 +/* draft-ietf-stewart-strreset-xxx */ +#define SCTP_STREAM_RESET 0x82 + +/* RFC4820 */ +#define SCTP_PAD_CHUNK 0x84 +/************0xc0 series ***********/ +/* RFC3758 */ +#define SCTP_FORWARD_CUM_TSN 0xc0 +/* RFC5061 */ +#define SCTP_ASCONF 0xc1 +#define SCTP_IFORWARD_CUM_TSN 0xc2 + +/* ABORT and SHUTDOWN COMPLETE FLAG */ +#define SCTP_HAD_NO_TCB 0x01 + +/* Packet dropped flags */ +#define SCTP_FROM_MIDDLE_BOX SCTP_HAD_NO_TCB +#define SCTP_BADCRC 0x02 +#define SCTP_PACKET_TRUNCATED 0x04 + +/* Flag for ECN -CWR */ +#define SCTP_CWR_REDUCE_OVERRIDE 0x01 +#define SCTP_CWR_IN_SAME_WINDOW 0x02 + +#define SCTP_SAT_NETWORK_MIN 400 /* min ms for RTT to set satellite + * time */ +#define SCTP_SAT_NETWORK_BURST_INCR 2 /* how many times to multiply maxburst + * in sat */ + +/* Data Chuck Specific Flags */ +#define SCTP_DATA_FRAG_MASK 0x03 +#define SCTP_DATA_MIDDLE_FRAG 0x00 +#define SCTP_DATA_LAST_FRAG 0x01 +#define SCTP_DATA_FIRST_FRAG 0x02 +#define SCTP_DATA_NOT_FRAG 0x03 +#define SCTP_DATA_UNORDERED 0x04 +#define SCTP_DATA_SACK_IMMEDIATELY 0x08 +/* ECN Nonce: SACK Chunk Specific Flags */ +#define SCTP_SACK_NONCE_SUM 0x01 + +/* CMT DAC algorithm SACK flag */ +#define SCTP_SACK_CMT_DAC 0x80 + +/* + * PCB flags (in sctp_flags bitmask). + * Note the features and flags are meant + * for use by netstat. + */ +#define SCTP_PCB_FLAGS_UDPTYPE 0x00000001 +#define SCTP_PCB_FLAGS_TCPTYPE 0x00000002 +#define SCTP_PCB_FLAGS_BOUNDALL 0x00000004 +#define SCTP_PCB_FLAGS_ACCEPTING 0x00000008 +#define SCTP_PCB_FLAGS_UNBOUND 0x00000010 +#define SCTP_PCB_FLAGS_SND_ITERATOR_UP 0x00000020 +#define SCTP_PCB_FLAGS_CLOSE_IP 0x00040000 +#define SCTP_PCB_FLAGS_WAS_CONNECTED 0x00080000 +#define SCTP_PCB_FLAGS_WAS_ABORTED 0x00100000 +/* TCP model support */ + +#define SCTP_PCB_FLAGS_CONNECTED 0x00200000 +#define SCTP_PCB_FLAGS_IN_TCPPOOL 0x00400000 +#define SCTP_PCB_FLAGS_DONT_WAKE 0x00800000 +#define SCTP_PCB_FLAGS_WAKEOUTPUT 0x01000000 +#define SCTP_PCB_FLAGS_WAKEINPUT 0x02000000 +#define SCTP_PCB_FLAGS_BOUND_V6 0x04000000 +#define SCTP_PCB_FLAGS_BLOCKING_IO 0x08000000 +#define SCTP_PCB_FLAGS_SOCKET_GONE 0x10000000 +#define SCTP_PCB_FLAGS_SOCKET_ALLGONE 0x20000000 +#define SCTP_PCB_FLAGS_SOCKET_CANT_READ 0x40000000 +#if defined(__Userspace__) +#define SCTP_PCB_FLAGS_BOUND_CONN 0x80000000 + +/* flags to copy to new PCB */ +#define SCTP_PCB_COPY_FLAGS (SCTP_PCB_FLAGS_BOUNDALL|\ + SCTP_PCB_FLAGS_WAKEINPUT|\ + SCTP_PCB_FLAGS_BOUND_V6|\ + SCTP_PCB_FLAGS_BOUND_CONN) +#else + +/* flags to copy to new PCB */ +#define SCTP_PCB_COPY_FLAGS (SCTP_PCB_FLAGS_BOUNDALL|\ + SCTP_PCB_FLAGS_WAKEINPUT|\ + SCTP_PCB_FLAGS_BOUND_V6) +#endif + +/* + * PCB Features (in sctp_features bitmask) + */ +#define SCTP_PCB_FLAGS_DO_NOT_PMTUD 0x0000000000000001 +#define SCTP_PCB_FLAGS_EXT_RCVINFO 0x0000000000000002 /* deprecated */ +#define SCTP_PCB_FLAGS_DONOT_HEARTBEAT 0x0000000000000004 +#define SCTP_PCB_FLAGS_FRAG_INTERLEAVE 0x0000000000000008 +#define SCTP_PCB_FLAGS_INTERLEAVE_STRMS 0x0000000000000010 +#define SCTP_PCB_FLAGS_DO_ASCONF 0x0000000000000020 +#define SCTP_PCB_FLAGS_AUTO_ASCONF 0x0000000000000040 +/* socket options */ +#define SCTP_PCB_FLAGS_NODELAY 0x0000000000000100 +#define SCTP_PCB_FLAGS_AUTOCLOSE 0x0000000000000200 +#define SCTP_PCB_FLAGS_RECVDATAIOEVNT 0x0000000000000400 /* deprecated */ +#define SCTP_PCB_FLAGS_RECVASSOCEVNT 0x0000000000000800 +#define SCTP_PCB_FLAGS_RECVPADDREVNT 0x0000000000001000 +#define SCTP_PCB_FLAGS_RECVPEERERR 0x0000000000002000 +#define SCTP_PCB_FLAGS_RECVSENDFAILEVNT 0x0000000000004000 /* deprecated */ +#define SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT 0x0000000000008000 +#define SCTP_PCB_FLAGS_ADAPTATIONEVNT 0x0000000000010000 +#define SCTP_PCB_FLAGS_PDAPIEVNT 0x0000000000020000 +#define SCTP_PCB_FLAGS_AUTHEVNT 0x0000000000040000 +#define SCTP_PCB_FLAGS_STREAM_RESETEVNT 0x0000000000080000 +#define SCTP_PCB_FLAGS_NO_FRAGMENT 0x0000000000100000 +#define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x0000000000400000 +#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x0000000000800000 +#define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x0000000001000000 +#define SCTP_PCB_FLAGS_PORTREUSE 0x0000000002000000 +#define SCTP_PCB_FLAGS_DRYEVNT 0x0000000004000000 +#define SCTP_PCB_FLAGS_RECVRCVINFO 0x0000000008000000 +#define SCTP_PCB_FLAGS_RECVNXTINFO 0x0000000010000000 +#define SCTP_PCB_FLAGS_ASSOC_RESETEVNT 0x0000000020000000 +#define SCTP_PCB_FLAGS_STREAM_CHANGEEVNT 0x0000000040000000 +#define SCTP_PCB_FLAGS_RECVNSENDFAILEVNT 0x0000000080000000 + +/*- + * mobility_features parameters (by micchie).Note + * these features are applied against the + * sctp_mobility_features flags.. not the sctp_features + * flags. + */ +#define SCTP_MOBILITY_BASE 0x00000001 +#define SCTP_MOBILITY_FASTHANDOFF 0x00000002 +#define SCTP_MOBILITY_PRIM_DELETED 0x00000004 + +/* Smallest PMTU allowed when disabling PMTU discovery */ +#define SCTP_SMALLEST_PMTU 512 +/* Largest PMTU allowed when disabling PMTU discovery */ +#define SCTP_LARGEST_PMTU 65536 + +#if defined(_WIN32) +#pragma pack(pop) +#endif +#undef SCTP_PACKED + +/* This dictates the size of the packet + * collection buffer. This only applies + * if SCTP_PACKET_LOGGING is enabled in + * your config. + */ +#define SCTP_PACKET_LOG_SIZE 65536 + +/* Maximum delays and such a user can set for options that + * take ms. + */ +#define SCTP_MAX_SACK_DELAY 500 /* per RFC4960 */ +#define SCTP_MAX_HB_INTERVAL 14400000 /* 4 hours in ms */ +#define SCTP_MIN_COOKIE_LIFE 1000 /* 1 second in ms */ +#define SCTP_MAX_COOKIE_LIFE 3600000 /* 1 hour in ms */ + +/* Types of logging/KTR tracing that can be enabled via the + * sysctl net.inet.sctp.sctp_logging. You must also enable + * SUBSYS tracing. + * Note that you must have the SCTP option in the kernel + * to enable these as well. + */ +#define SCTP_BLK_LOGGING_ENABLE 0x00000001 +#define SCTP_CWND_MONITOR_ENABLE 0x00000002 +#define SCTP_CWND_LOGGING_ENABLE 0x00000004 +#define SCTP_FLIGHT_LOGGING_ENABLE 0x00000020 +#define SCTP_FR_LOGGING_ENABLE 0x00000040 +#define SCTP_LOCK_LOGGING_ENABLE 0x00000080 +#define SCTP_MAP_LOGGING_ENABLE 0x00000100 +#define SCTP_MBCNT_LOGGING_ENABLE 0x00000200 +#define SCTP_MBUF_LOGGING_ENABLE 0x00000400 +#define SCTP_NAGLE_LOGGING_ENABLE 0x00000800 +#define SCTP_RECV_RWND_LOGGING_ENABLE 0x00001000 +#define SCTP_RTTVAR_LOGGING_ENABLE 0x00002000 +#define SCTP_SACK_LOGGING_ENABLE 0x00004000 +#define SCTP_SACK_RWND_LOGGING_ENABLE 0x00008000 +#define SCTP_SB_LOGGING_ENABLE 0x00010000 +#define SCTP_STR_LOGGING_ENABLE 0x00020000 +#define SCTP_WAKE_LOGGING_ENABLE 0x00040000 +#define SCTP_LOG_MAXBURST_ENABLE 0x00080000 +#define SCTP_LOG_RWND_ENABLE 0x00100000 +#define SCTP_LOG_SACK_ARRIVALS_ENABLE 0x00200000 +#define SCTP_LTRACE_CHUNK_ENABLE 0x00400000 +#define SCTP_LTRACE_ERROR_ENABLE 0x00800000 +#define SCTP_LAST_PACKET_TRACING 0x01000000 +#define SCTP_THRESHOLD_LOGGING 0x02000000 +#define SCTP_LOG_AT_SEND_2_SCTP 0x04000000 +#define SCTP_LOG_AT_SEND_2_OUTQ 0x08000000 +#define SCTP_LOG_TRY_ADVANCE 0x10000000 + +#endif /* !_NETINET_SCTP_H_ */ diff --git a/include/timer.h b/include/timer.h index ba1bb6563..a00f09fb4 100644 --- a/include/timer.h +++ b/include/timer.h @@ -17,6 +17,7 @@ */ #ifndef __DPVS_TIMER_H__ #define __DPVS_TIMER_H__ +#include #include #include "list.h" diff --git a/kmod/toa/example_nat64/nginx/README.md b/kmod/toa/example_nat64/nginx/README.md deleted file mode 100644 index 3e3190ee9..000000000 --- a/kmod/toa/example_nat64/nginx/README.md +++ /dev/null @@ -1,32 +0,0 @@ -This patch is for Nginx to get real client ip by 'toa_remote_addr' -when you are using NAT64 mode(VIP is IPv6 while RS is IPv4). -You can use this patch only when toa module is installed. - -Here is an exampe to configure http block in nginx.conf: - -``` -http { - include mime.types; - default_type application/octet-stream; - - log_format main '$toa_remote_addr $toa_remote_port $remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /data/nginx/logs/access.log main; - - keepalive_timeout 65; - - server { - listen 80; - server_name localhost; - - access_log /data/nginx/logs/access.log main; - - location / { - proxy_set_header X-Forwarded-For $toa_remote_addr; - proxy_pass http://192.168.1.1; - } - } -} -``` diff --git a/kmod/uoa/uoa.c b/kmod/uoa/uoa.c index 28d487988..5e61b3c00 100644 --- a/kmod/uoa/uoa.c +++ b/kmod/uoa/uoa.c @@ -199,7 +199,13 @@ static int uoa_stats_percpu_show(struct seq_file *seq, void *arg) unsigned int start; do { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0) || \ + ( defined(RHEL_MAJOR) && ((RHEL_MAJOR == 9 && RHEL_MINOR > 3) || RHEL_MAJOR > 9)) + start = u64_stats_fetch_begin(&s->syncp); +#else start = u64_stats_fetch_begin_irq(&s->syncp); +#endif #endif success = s->success; miss = s->miss; @@ -209,7 +215,12 @@ static int uoa_stats_percpu_show(struct seq_file *seq, void *arg) saved = s->uoa_saved; ack_fail = s->uoa_ack_fail; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0) || \ + ( defined(RHEL_MAJOR) && ((RHEL_MAJOR == 9 && RHEL_MINOR > 3) || RHEL_MAJOR > 9)) + } while (u64_stats_fetch_retry(&s->syncp, start)); +#else } while (u64_stats_fetch_retry_irq(&s->syncp, start)); +#endif #endif seq_printf(seq, diff --git a/patch/dpdk-stable-20.11.1/0007-bonding-device-sends-packets-with-user-specified-sal.patch b/patch/dpdk-stable-20.11.1/0007-bonding-device-sends-packets-with-user-specified-sal.patch new file mode 100644 index 000000000..d7e4e0c6d --- /dev/null +++ b/patch/dpdk-stable-20.11.1/0007-bonding-device-sends-packets-with-user-specified-sal.patch @@ -0,0 +1,91 @@ +From 7024d80414e914a54c301dbcc9bb4cf6fb5f927b Mon Sep 17 00:00:00 2001 +From: yuwenchao +Date: Tue, 30 Jul 2024 15:39:28 +0800 +Subject: [PATCH] bonding device sends packets with user specified salve port + +The outgoing slave port is specified in mbuf field "hash.txadapter.reserved2". +Support the following 3 bonding mode: +- mode 0: round robin +- mode 2: balance +- mode 4: 8023ad + +Signed-off-by: yuwenchao +--- + drivers/net/bonding/rte_eth_bond_pmd.c | 26 ++++++++++++++++++++++++-- + lib/librte_mbuf/rte_mbuf.h | 2 ++ + 2 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c +index 42e436c..a35422c 100644 +--- a/drivers/net/bonding/rte_eth_bond_pmd.c ++++ b/drivers/net/bonding/rte_eth_bond_pmd.c +@@ -573,6 +573,22 @@ struct client_stats_t { + return nb_recv_pkts; + } + ++static inline int ++bond_ethdev_populate_slave_by_user(const struct rte_mbuf *mbuf, const uint16_t *slaves, ++ int num_slave) ++{ ++ uint16_t i, pid = mbuf->hash.txadapter.reserved2; ++ ++ if (likely(pid == RTE_MBUF_PORT_INVALID)) ++ return -1; ++ ++ for (i = 0; i < num_slave; i++) { ++ if (slaves[i] == pid) ++ return i; ++ } ++ return -1; ++} ++ + static uint16_t + bond_ethdev_tx_burst_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +@@ -605,7 +621,9 @@ struct client_stats_t { + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { +- cslave_idx = (slave_idx + i) % num_of_slaves; ++ cslave_idx = bond_ethdev_populate_slave_by_user(bufs[i], slaves, num_of_slaves); ++ if (likely(cslave_idx < 0)) ++ cslave_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cslave_idx][(slave_nb_pkts[cslave_idx])++] = bufs[i]; + } + +@@ -1162,7 +1180,11 @@ struct bwg_slave { + + for (i = 0; i < nb_bufs; i++) { + /* Populate slave mbuf arrays with mbufs for that slave. */ +- uint16_t slave_idx = bufs_slave_port_idxs[i]; ++ int slave_idx; ++ ++ slave_idx = bond_ethdev_populate_slave_by_user(bufs[i], slave_port_ids, slave_count); ++ if (likely(slave_idx < 0)) ++ slave_idx = bufs_slave_port_idxs[i]; + + slave_bufs[slave_idx][slave_nb_bufs[slave_idx]++] = bufs[i]; + } +diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h +index c4c9ebf..130b99d 100644 +--- a/lib/librte_mbuf/rte_mbuf.h ++++ b/lib/librte_mbuf/rte_mbuf.h +@@ -589,6 +589,7 @@ static inline struct rte_mbuf *rte_mbuf_raw_alloc(struct rte_mempool *mp) + + if (rte_mempool_get(mp, (void **)&m) < 0) + return NULL; ++ m->hash.txadapter.reserved2 = RTE_MBUF_PORT_INVALID; + __rte_mbuf_raw_sanity_check(m); + return m; + } +@@ -867,6 +868,7 @@ static inline void rte_pktmbuf_reset(struct rte_mbuf *m) + m->vlan_tci_outer = 0; + m->nb_segs = 1; + m->port = RTE_MBUF_PORT_INVALID; ++ m->hash.txadapter.reserved2 = RTE_MBUF_PORT_INVALID; + + m->ol_flags &= EXT_ATTACHED_MBUF; + m->packet_type = 0; +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0001-kni-use-netlink-event-for-multicast-driver-part.patch b/patch/dpdk-stable-20.11.10/0001-kni-use-netlink-event-for-multicast-driver-part.patch new file mode 100644 index 000000000..8f9c28635 --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0001-kni-use-netlink-event-for-multicast-driver-part.patch @@ -0,0 +1,128 @@ +From 3e182c106d61863a55e35425e2afefcc222f8f92 Mon Sep 17 00:00:00 2001 +From: yuwenchao +Date: Thu, 1 Aug 2024 17:18:30 +0800 +Subject: [PATCH 1/7] kni: use netlink event for multicast (driver part) + +Signed-off-by: yuwenchao +--- + kernel/linux/kni/kni_net.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 76 insertions(+) + +diff --git a/kernel/linux/kni/kni_net.c b/kernel/linux/kni/kni_net.c +index 779ee34..31e9e39 100644 +--- a/kernel/linux/kni/kni_net.c ++++ b/kernel/linux/kni/kni_net.c +@@ -17,6 +17,8 @@ + #include + #include + #include ++#include ++#include + #include + + #include +@@ -147,6 +149,7 @@ + ret_val = wait_event_interruptible_timeout(kni->wq, + kni_fifo_count(kni->resp_q), 3 * HZ); + if (signal_pending(current) || ret_val <= 0) { ++ pr_err("%s: wait_event_interruptible timeout\n", __func__); + ret = -ETIME; + goto fail; + } +@@ -690,6 +693,77 @@ void kni_net_release_fifo_phy(struct kni_dev *kni) + return (ret == 0) ? req.result : ret; + } + ++static size_t ++kni_nlmsg_size(void) ++{ ++ return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) ++ + nla_total_size(4) /* IFA_ADDRESS */ ++ + nla_total_size(4) /* IFA_LOCAL */ ++ + nla_total_size(4) /* IFA_BROADCAST */ ++ + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ ++ + nla_total_size(4) /* IFA_FLAGS */ ++ + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ ++} ++ ++static void ++kni_net_set_rx_mode(struct net_device *dev) ++{ ++ /* ++ * send event to notify user (DPDK KNI app) that multicast list changed, ++ * so that it can monitor multicast join/leave and set HW mc-addrs to ++ * kni dev accordinglly. ++ * ++ * this event is just an notification, we do not save any mc-addr here ++ * (so attribute space for us). user kni app should get maddrs after ++ * receive this notification. ++ * ++ * I was expecting kernel send some rtnl event for multicast join/leave, ++ * but it doesn't. By checking the call-chain of SIOCADDMULTI (ip maddr, ++ * manages only hardware multicast) and IP_ADD_MEMBERSHIP (ip_mc_join_group, ++ * used to for IPv4 multicast), no rtnl event sent. ++ * ++ * so as workaround, modify kni driver here to send RTM_NEWADDR. ++ * it may not suitalbe to use this event for mcast, but that should works. ++ * hope that won't affect other listener to this event. ++ * ++ * previous solution was using rte_kni_request to pass hw-maddr list to user. ++ * it "works" for times but finally memory corruption found, which is ++ * not easy to address (lock was added and reviewed). That's why we use ++ * netlink event instead. ++ */ ++ struct sk_buff *skb; ++ struct net *net = dev_net(dev); ++ struct nlmsghdr *nlh; ++ struct ifaddrmsg *ifm; ++ ++ skb = nlmsg_new(kni_nlmsg_size(), GFP_ATOMIC); ++ if (!skb) ++ return; ++ ++ /* no other event for us ? */ ++ nlh = nlmsg_put(skb, 0, 0, RTM_NEWADDR, sizeof(*ifm), 0); ++ if (!nlh) { ++ kfree_skb(skb); ++ return; ++ } ++ ++ /* just send an notification so no other info */ ++ ifm = nlmsg_data(nlh); ++ memset(ifm, 0, sizeof(*ifm)); ++ ifm->ifa_family = AF_UNSPEC; ++ ifm->ifa_prefixlen = 0; ++ ifm->ifa_flags = 0; ++ ifm->ifa_scope = RT_SCOPE_NOWHERE; ++ ifm->ifa_index = 0; ++ ++ nlmsg_end(skb, nlh); ++ ++ /* other group ? */ ++ pr_debug("%s: rx-mode/multicast-list changed\n", __func__); ++ rtnl_notify(skb, net, 0, RTNLGRP_NOTIFY, NULL, GFP_ATOMIC); ++ return; ++} ++ + static void + kni_net_change_rx_flags(struct net_device *netdev, int flags) + { +@@ -791,6 +865,7 @@ void kni_net_release_fifo_phy(struct kni_dev *kni) + + ret = kni_net_process_request(netdev, &req); + ++ pr_info("%s request returns %d!\n", __func__, ret); + return (ret == 0 ? req.result : ret); + } + +@@ -822,6 +897,7 @@ void kni_net_release_fifo_phy(struct kni_dev *kni) + .ndo_change_rx_flags = kni_net_change_rx_flags, + .ndo_start_xmit = kni_net_tx, + .ndo_change_mtu = kni_net_change_mtu, ++ .ndo_set_rx_mode = kni_net_set_rx_mode, + .ndo_tx_timeout = kni_net_tx_timeout, + .ndo_set_mac_address = kni_net_set_mac, + #ifdef HAVE_CHANGE_CARRIER_CB +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0002-pdump-change-dpdk-pdump-tool-for-dpvs.patch b/patch/dpdk-stable-20.11.10/0002-pdump-change-dpdk-pdump-tool-for-dpvs.patch new file mode 100644 index 000000000..5d71abd55 --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0002-pdump-change-dpdk-pdump-tool-for-dpvs.patch @@ -0,0 +1,555 @@ +From 288a252c8b65ea6c811100b3472367891f298f7d Mon Sep 17 00:00:00 2001 +From: huangyichen +Date: Thu, 1 Jul 2021 21:23:50 +0800 +Subject: [PATCH 2/7] pdump: change dpdk-pdump tool for dpvs + +--- + app/pdump/main.c | 167 ++++++++++++++++++++++++++++++++++++++++--- + lib/librte_pdump/rte_pdump.c | 145 +++++++++++++++++++++++++++++++++++-- + lib/librte_pdump/rte_pdump.h | 27 +++++++ + 3 files changed, 327 insertions(+), 12 deletions(-) + +diff --git a/app/pdump/main.c b/app/pdump/main.c +index 36b14fa..5b4217e 100644 +--- a/app/pdump/main.c ++++ b/app/pdump/main.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #define CMD_LINE_OPT_PDUMP "pdump" + #define CMD_LINE_OPT_PDUMP_NUM 256 +@@ -42,6 +43,14 @@ + #define PDUMP_MSIZE_ARG "mbuf-size" + #define PDUMP_NUM_MBUFS_ARG "total-num-mbufs" + ++#define PDUMP_HOST_ARG "host" ++#define PDUMP_SRC_ARG "src-host" ++#define PDUMP_DST_ARG "dst-host" ++#define PDUMP_PROTO_PORT_AGE "proto-port" ++#define PDUMP_SPORT_ARG "src-port" ++#define PDUMP_DPORT_ARG "dst-port" ++#define PDUMP_PROTO_ARG "proto" ++ + #define VDEV_NAME_FMT "net_pcap_%s_%d" + #define VDEV_PCAP_ARGS_FMT "tx_pcap=%s" + #define VDEV_IFACE_ARGS_FMT "tx_iface=%s" +@@ -97,6 +106,13 @@ enum pdump_by { + PDUMP_RING_SIZE_ARG, + PDUMP_MSIZE_ARG, + PDUMP_NUM_MBUFS_ARG, ++ PDUMP_HOST_ARG, ++ PDUMP_SRC_ARG, ++ PDUMP_DST_ARG, ++ PDUMP_PROTO_PORT_AGE, ++ PDUMP_SPORT_ARG, ++ PDUMP_DPORT_ARG, ++ PDUMP_PROTO_ARG, + NULL + }; + +@@ -130,6 +146,7 @@ struct pdump_tuples { + enum pcap_stream rx_vdev_stream_type; + enum pcap_stream tx_vdev_stream_type; + bool single_pdump_dev; ++ struct pdump_filter *filter; + + /* stats */ + struct pdump_stats stats; +@@ -158,6 +175,11 @@ struct parse_val { + "(queue=)," + "(rx-dev= |" + " tx-dev=," ++ "[host= | src-host= |" ++ "dst-host=]," ++ "[proto=support:tcp/udp/icmp]," ++ "[proto-port= |src-port= |" ++ "dst-port=]," + "[ring-size=default:16384]," + "[mbuf-size=default:2176]," + "[total-num-mbufs=default:65535]'\n", +@@ -244,6 +266,64 @@ struct parse_val { + } + + static int ++parse_host(const char *key __rte_unused, const char *value, void *extra_args) ++{ ++ struct pdump_tuples *pt = extra_args; ++ struct in_addr inaddr; ++ struct in6_addr inaddr6; ++ union addr addr; ++ int af = 0; ++ ++ if (inet_pton(AF_INET6, value, &inaddr6) > 0) { ++ af = AF_INET6; ++ addr.in6 = inaddr6; ++ } else if (inet_pton(AF_INET, value, &inaddr) > 0){ ++ af = AF_INET; ++ addr.in = inaddr; ++ } else { ++ printf("IP address invaled\n"); ++ return -EINVAL; ++ } ++ ++ if (pt->filter && pt->filter->af != 0 && af != pt->filter->af) { ++ printf("IPv4 and IPv6 conflict\n"); ++ return -EINVAL; ++ } else { ++ pt->filter->af = af; ++ } ++ ++ if (!strcmp(key, PDUMP_HOST_ARG)) { ++ rte_memcpy(&pt->filter->host_addr, &addr, sizeof(addr)); ++ } else if (!strcmp(key, PDUMP_SRC_ARG)) { ++ rte_memcpy(&pt->filter->s_addr, &addr, sizeof(addr)); ++ } else if (!strcmp(key, PDUMP_DST_ARG)) { ++ rte_memcpy(&pt->filter->d_addr, &addr, sizeof(addr)); ++ } ++ ++ return 0; ++} ++ ++static int ++parse_proto(const char *key __rte_unused, const char *value, void *extra_args) ++{ ++ struct pdump_tuples *pt = extra_args; ++ ++ if (!strcmp(value, "tcp")) { ++ pt->filter->proto = IPPROTO_TCP; ++ } else if (!strcmp(value, "udp")) { ++ pt->filter->proto = IPPROTO_UDP; ++ } else if (!strcmp(value, "icmp")) { ++ pt->filter->proto = IPPROTO_ICMP; ++ } else { ++ printf("invalid value:\"%s\" for key:\"%s\", " ++ "value must be tcp/udp/icmp\n", value, key); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int + parse_pdump(const char *optarg) + { + struct rte_kvargs *kvlist; +@@ -370,6 +450,75 @@ struct parse_val { + } else + pt->total_num_mbufs = MBUFS_PER_POOL; + ++ /* filter parsing and validation */ ++ pt->filter = rte_zmalloc("pdump_filter", ++ sizeof(struct pdump_filter), 0); ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_HOST_ARG); ++ if (cnt1 == 1) { ++ ret = rte_kvargs_process(kvlist, PDUMP_HOST_ARG, ++ &parse_host, pt); ++ if (ret < 0) ++ goto free_kvlist; ++ } ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_SRC_ARG); ++ if (cnt1 == 1) { ++ ret = rte_kvargs_process(kvlist, PDUMP_SRC_ARG, ++ &parse_host, pt); ++ if (ret < 0) ++ goto free_kvlist; ++ } ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_DST_ARG); ++ if (cnt1 == 1) { ++ ret = rte_kvargs_process(kvlist, PDUMP_DST_ARG, ++ &parse_host, pt); ++ if (ret < 0) ++ goto free_kvlist; ++ } ++ ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_PROTO_PORT_AGE); ++ if (cnt1 == 1) { ++ v.min = 1; ++ v.max = UINT16_MAX; ++ ret = rte_kvargs_process(kvlist, PDUMP_PROTO_PORT_AGE, ++ &parse_uint_value, &v); ++ if (ret < 0) ++ goto free_kvlist; ++ pt->filter->proto_port = (uint16_t) v.val; ++ } ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_SPORT_ARG); ++ if (cnt1 == 1) { ++ v.min = 1; ++ v.max = UINT16_MAX; ++ ret = rte_kvargs_process(kvlist, PDUMP_SPORT_ARG, ++ &parse_uint_value, &v); ++ if (ret < 0) ++ goto free_kvlist; ++ pt->filter->s_port = (uint16_t) v.val; ++ } ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_DPORT_ARG); ++ if (cnt1 == 1) { ++ v.min = 1; ++ v.max = UINT16_MAX; ++ ret = rte_kvargs_process(kvlist, PDUMP_DPORT_ARG, ++ &parse_uint_value, &v); ++ if (ret < 0) ++ goto free_kvlist; ++ pt->filter->d_port = (uint16_t) v.val; ++ } ++ ++ cnt1 = rte_kvargs_count(kvlist, PDUMP_PROTO_ARG); ++ if (cnt1 == 1) { ++ ret = rte_kvargs_process(kvlist, PDUMP_PROTO_ARG, ++ &parse_proto, pt); ++ if (ret < 0) ++ goto free_kvlist; ++ } ++ + num_tuples++; + + free_kvlist: +@@ -510,6 +659,8 @@ struct parse_val { + rte_ring_free(pt->rx_ring); + if (pt->tx_ring) + rte_ring_free(pt->tx_ring); ++ if (pt->filter) ++ rte_free(pt->filter); + } + } + +@@ -837,20 +988,20 @@ struct parse_val { + pt->queue, + RTE_PDUMP_FLAG_RX, + pt->rx_ring, +- pt->mp, NULL); ++ pt->mp, pt->filter); + ret1 = rte_pdump_enable_by_deviceid( + pt->device_id, + pt->queue, + RTE_PDUMP_FLAG_TX, + pt->tx_ring, +- pt->mp, NULL); ++ pt->mp, pt->filter); + } else if (pt->dump_by_type == PORT_ID) { + ret = rte_pdump_enable(pt->port, pt->queue, + RTE_PDUMP_FLAG_RX, +- pt->rx_ring, pt->mp, NULL); ++ pt->rx_ring, pt->mp, pt->filter); + ret1 = rte_pdump_enable(pt->port, pt->queue, + RTE_PDUMP_FLAG_TX, +- pt->tx_ring, pt->mp, NULL); ++ pt->tx_ring, pt->mp, pt->filter); + } + } else if (pt->dir == RTE_PDUMP_FLAG_RX) { + if (pt->dump_by_type == DEVICE_ID) +@@ -858,22 +1009,22 @@ struct parse_val { + pt->device_id, + pt->queue, + pt->dir, pt->rx_ring, +- pt->mp, NULL); ++ pt->mp, pt->filter); + else if (pt->dump_by_type == PORT_ID) + ret = rte_pdump_enable(pt->port, pt->queue, + pt->dir, +- pt->rx_ring, pt->mp, NULL); ++ pt->rx_ring, pt->mp, pt->filter); + } else if (pt->dir == RTE_PDUMP_FLAG_TX) { + if (pt->dump_by_type == DEVICE_ID) + ret = rte_pdump_enable_by_deviceid( + pt->device_id, + pt->queue, + pt->dir, +- pt->tx_ring, pt->mp, NULL); ++ pt->tx_ring, pt->mp, pt->filter); + else if (pt->dump_by_type == PORT_ID) + ret = rte_pdump_enable(pt->port, pt->queue, + pt->dir, +- pt->tx_ring, pt->mp, NULL); ++ pt->tx_ring, pt->mp, pt->filter); + } + if (ret < 0 || ret1 < 0) { + cleanup_pdump_resources(); +diff --git a/lib/librte_pdump/rte_pdump.c b/lib/librte_pdump/rte_pdump.c +index 746005a..8a252d5 100644 +--- a/lib/librte_pdump/rte_pdump.c ++++ b/lib/librte_pdump/rte_pdump.c +@@ -9,6 +9,10 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + + #include "rte_pdump.h" + +@@ -69,6 +73,132 @@ struct pdump_response { + } rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT], + tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; + ++static int ++inet_addr_equal(int af, const union addr *a1, ++ const union addr *a2) ++{ ++ switch (af) { ++ case AF_INET: ++ return a1->in.s_addr == a2->in.s_addr; ++ case AF_INET6: ++ return memcmp(a1->in6.s6_addr, a2->in6.s6_addr, 16) == 0; ++ default: ++ return memcmp(a1, a2, sizeof(union addr)) == 0; ++ } ++} ++ ++static int ++inet_is_addr_any(int af, const union addr *addr) ++{ ++ switch (af) { ++ case AF_INET: ++ return addr->in.s_addr == htonl(INADDR_ANY); ++ case AF_INET6: ++ return IN6_ARE_ADDR_EQUAL(&addr->in6, &in6addr_any); ++ default: ++ return -1; ++ } ++ ++ return -1; ++} ++static int ++pdump_filter(struct rte_mbuf *m, struct pdump_filter *filter) ++{ ++ struct rte_ether_hdr *eth_hdr; ++ struct vlan_eth_hdr *vlan_eth_hdr; ++ union addr s_addr, d_addr; ++ int prepend = 0; ++ uint16_t type = 0; ++ uint16_t iph_len = 0; ++ uint8_t proto = 0; ++ ++ int af; ++ ++ if (filter->af == 0 && filter->s_port == 0 && ++ filter->d_port == 0 && filter->proto == 0 && ++ filter->proto_port == 0) ++ return 0; ++ ++ eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); ++ ++ if (eth_hdr->ether_type == htons(ETH_P_8021Q)) { ++ prepend += sizeof(struct vlan_eth_hdr); ++ vlan_eth_hdr = rte_pktmbuf_mtod(m, struct vlan_eth_hdr *); ++ type = vlan_eth_hdr->h_vlan_encapsulated_proto; ++ } else { ++ prepend += sizeof(struct rte_ether_hdr); ++ eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); ++ type = eth_hdr->ether_type; ++ } ++ ++ if (rte_pktmbuf_adj(m, prepend) == NULL) ++ goto prepend; ++ ++ if (type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) { ++ struct rte_arp_hdr *arp = rte_pktmbuf_mtod(m, struct rte_arp_hdr *); ++ af = AF_INET; ++ s_addr.in.s_addr = arp->arp_data.arp_sip; ++ d_addr.in.s_addr = arp->arp_data.arp_tip; ++ } else if (type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) { ++ struct rte_ipv4_hdr *ip4 = rte_pktmbuf_mtod(m, struct rte_ipv4_hdr *); ++ af = AF_INET; ++ s_addr.in.s_addr = ip4->src_addr; ++ d_addr.in.s_addr = ip4->dst_addr; ++ proto = ip4->next_proto_id; ++ iph_len = (ip4->version_ihl & 0xf) << 2; ++ } else if (type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) { ++ struct rte_ipv6_hdr *ip6 = rte_pktmbuf_mtod(m, struct rte_ipv6_hdr *); ++ af = AF_INET6; ++ rte_memcpy(&s_addr.in6, &ip6->src_addr, 16); ++ rte_memcpy(&d_addr.in6, &ip6->dst_addr, 16); ++ proto = ip6->proto; ++ iph_len = sizeof(struct rte_ipv6_hdr); ++ } else { ++ goto prepend; ++ } ++ ++ /*filter*/ ++ if (!inet_is_addr_any(af, &filter->s_addr) && ++ !inet_addr_equal(af, &filter->s_addr, &s_addr)) ++ goto prepend; ++ if (!inet_is_addr_any(af, &filter->d_addr) && ++ !inet_addr_equal(af, &filter->d_addr, &d_addr)) ++ goto prepend; ++ if (!inet_is_addr_any(af, &filter->host_addr) && ++ !inet_addr_equal(af, &filter->host_addr, &s_addr) && ++ !inet_addr_equal(af, &filter->host_addr, &d_addr)) ++ goto prepend; ++ ++ if (filter->proto && filter->proto != proto) ++ goto prepend; ++ ++ if (filter->s_port || filter->d_port || filter->proto_port) { ++ if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) ++ goto prepend; ++ struct rte_udp_hdr _uh; ++ const struct rte_udp_hdr *uh; ++ uh = rte_pktmbuf_read(m, iph_len, sizeof(_uh), &_uh); ++ if (uh == NULL) ++ goto prepend; ++ if (filter->s_port && filter->s_port != rte_cpu_to_be_16(uh->src_port)) ++ goto prepend; ++ ++ if (filter->d_port && filter->d_port != rte_cpu_to_be_16(uh->dst_port)) ++ goto prepend; ++ ++ if (filter->proto_port && ++ filter->proto_port != rte_cpu_to_be_16(uh->src_port) && ++ filter->proto_port != rte_cpu_to_be_16(uh->dst_port)) ++ goto prepend; ++ } ++ ++ rte_pktmbuf_prepend(m, prepend); ++ return 0; ++ ++prepend: ++ rte_pktmbuf_prepend(m, prepend); ++ return -1; ++} + + static inline void + pdump_copy(struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) +@@ -86,6 +216,8 @@ struct pdump_response { + ring = cbs->ring; + mp = cbs->mp; + for (i = 0; i < nb_pkts; i++) { ++ if (pdump_filter(pkts[i], cbs->filter) != 0) ++ continue; + p = rte_pktmbuf_copy(pkts[i], mp, 0, UINT32_MAX); + if (p) + dup_bufs[d_pkts++] = p; +@@ -122,7 +254,7 @@ struct pdump_response { + static int + pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, + struct rte_ring *ring, struct rte_mempool *mp, +- uint16_t operation) ++ struct pdump_filter *filter, uint16_t operation) + { + uint16_t qid; + struct pdump_rxtx_cbs *cbs = NULL; +@@ -140,6 +272,7 @@ struct pdump_response { + } + cbs->ring = ring; + cbs->mp = mp; ++ cbs->filter = filter; + cbs->cb = rte_eth_add_first_rx_callback(port, qid, + pdump_rx, cbs); + if (cbs->cb == NULL) { +@@ -176,7 +309,7 @@ struct pdump_response { + static int + pdump_register_tx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, + struct rte_ring *ring, struct rte_mempool *mp, +- uint16_t operation) ++ struct pdump_filter *filter, uint16_t operation) + { + + uint16_t qid; +@@ -195,6 +328,7 @@ struct pdump_response { + } + cbs->ring = ring; + cbs->mp = mp; ++ cbs->filter = filter; + cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx, + cbs); + if (cbs->cb == NULL) { +@@ -238,6 +372,7 @@ struct pdump_response { + uint16_t operation; + struct rte_ring *ring; + struct rte_mempool *mp; ++ struct pdump_filter *filter; + + flags = p->flags; + operation = p->op; +@@ -253,6 +388,7 @@ struct pdump_response { + queue = p->data.en_v1.queue; + ring = p->data.en_v1.ring; + mp = p->data.en_v1.mp; ++ filter = p->data.en_v1.filter; + } else { + ret = rte_eth_dev_get_port_by_name(p->data.dis_v1.device, + &port); +@@ -265,6 +401,7 @@ struct pdump_response { + queue = p->data.dis_v1.queue; + ring = p->data.dis_v1.ring; + mp = p->data.dis_v1.mp; ++ filter = p->data.dis_v1.filter; + } + + /* validation if packet capture is for all queues */ +@@ -303,7 +440,7 @@ struct pdump_response { + if (flags & RTE_PDUMP_FLAG_RX) { + end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1; + ret = pdump_register_rx_callbacks(end_q, port, queue, ring, mp, +- operation); ++ filter, operation); + if (ret < 0) + return ret; + } +@@ -312,7 +449,7 @@ struct pdump_response { + if (flags & RTE_PDUMP_FLAG_TX) { + end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1; + ret = pdump_register_tx_callbacks(end_q, port, queue, ring, mp, +- operation); ++ filter, operation); + if (ret < 0) + return ret; + } +diff --git a/lib/librte_pdump/rte_pdump.h b/lib/librte_pdump/rte_pdump.h +index 6b00fc1..3986b07 100644 +--- a/lib/librte_pdump/rte_pdump.h ++++ b/lib/librte_pdump/rte_pdump.h +@@ -15,6 +15,8 @@ + #include + #include + #include ++#include ++#include + + #ifdef __cplusplus + extern "C" { +@@ -29,6 +31,31 @@ enum { + RTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX) + }; + ++union addr { ++ struct in_addr in; ++ struct in6_addr in6; ++}; ++ ++struct pdump_filter { ++ int af; ++ union addr s_addr; ++ union addr d_addr; ++ union addr host_addr; //s_addr or d_addr ++ ++ uint8_t proto; ++ uint16_t proto_port; //s_port or d_port ++ uint16_t s_port; ++ uint16_t d_port; ++}; ++ ++struct vlan_eth_hdr { ++ unsigned char h_dest[ETH_ALEN]; ++ unsigned char h_source[ETH_ALEN]; ++ unsigned short h_vlan_proto; ++ unsigned short h_vlan_TCI; ++ unsigned short h_vlan_encapsulated_proto; ++}; ++ + /** + * Initialize packet capturing handling + * +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0003-debug-enable-dpdk-eal-memory-debug.patch b/patch/dpdk-stable-20.11.10/0003-debug-enable-dpdk-eal-memory-debug.patch new file mode 100644 index 000000000..77a18998e --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0003-debug-enable-dpdk-eal-memory-debug.patch @@ -0,0 +1,59 @@ +From 3263fcc900f9e97cf777cb1ad2d84408f6fe7bcf Mon Sep 17 00:00:00 2001 +From: huangyichen +Date: Thu, 1 Jul 2021 21:24:47 +0800 +Subject: [PATCH 3/7] debug: enable dpdk eal memory debug + +The patch is used for memory debug. To use the patch, configure meson with option +-Dc_args="-DRTE_MALLOC_DEBUG" when building dpdk. For example, + +meson -Dc_args="-DRTE_MALLOC_DEBUG" -Dbuildtype=debug -Dprefix=$(pwd)/dpdklib dpdkbuild +ninja -C dpdkbuild +--- + lib/librte_eal/common/rte_malloc.c | 4 ++++ + lib/librte_eal/include/rte_malloc.h | 15 +++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c +index 684af4e..cc7ebb6 100644 +--- a/lib/librte_eal/common/rte_malloc.c ++++ b/lib/librte_eal/common/rte_malloc.c +@@ -30,6 +30,10 @@ + #include "eal_memcfg.h" + #include "eal_private.h" + ++int rte_memmory_ok(void *addr) ++{ ++ return malloc_elem_cookies_ok(RTE_PTR_SUB(addr, MALLOC_ELEM_HEADER_LEN)); ++} + + /* Free the memory space back to heap */ + static void +diff --git a/lib/librte_eal/include/rte_malloc.h b/lib/librte_eal/include/rte_malloc.h +index c8da894..3756d0d 100644 +--- a/lib/librte_eal/include/rte_malloc.h ++++ b/lib/librte_eal/include/rte_malloc.h +@@ -248,6 +248,21 @@ struct rte_malloc_socket_stats { + __rte_alloc_size(2, 3); + + /** ++ * Check the header/tailer cookies of memory pointed to by the provided pointer. ++ * ++ * This pointer must have been returned by a previous call to ++ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). ++ * ++ * @param ptr ++ * The pointer to memory to be checked. ++ * @return ++ * - true if the header/tailer cookies are OK. ++ * - Otherwise, false. ++ */ ++int ++rte_memmory_ok(void *ptr); ++ ++/** + * Frees the memory space pointed to by the provided pointer. + * + * This pointer must have been returned by a previous call to +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0004-ixgbe_flow-patch-ixgbe-fdir-rte_flow-for-dpvs.patch b/patch/dpdk-stable-20.11.10/0004-ixgbe_flow-patch-ixgbe-fdir-rte_flow-for-dpvs.patch new file mode 100644 index 000000000..136ba76a9 --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0004-ixgbe_flow-patch-ixgbe-fdir-rte_flow-for-dpvs.patch @@ -0,0 +1,256 @@ +From 4b9735e0d479916ec0e7636e5440d4538b349148 Mon Sep 17 00:00:00 2001 +From: huangyichen +Date: Fri, 2 Jul 2021 11:55:47 +0800 +Subject: [PATCH 4/7] ixgbe_flow: patch ixgbe fdir rte_flow for dpvs + +1. Ignore fdir flow rule priority attribute. +2. Use different fdir soft-id for flow rules configured for the same queue. +3. Disable fdir mask settings by rte_flow. +4. Allow IPv6 to pass flow rule ETH item validation. +5. TCP & UDP flow item dest port = 0 is invalid of ixgbe_parse_ntuple_filter() +6. Safe free ixgbe_flow_list item of MARCO RTE_MALLOC_DEBUG is define (configure meson with option -Dc_args="-DRTE_MALLOC_DEBUG") +--- + drivers/net/ixgbe/ixgbe_flow.c | 119 ++++++++++++++++++++++++++++++++++++----- + 1 file changed, 105 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ixgbe/ixgbe_flow.c b/drivers/net/ixgbe/ixgbe_flow.c +index 7e5b684..e9bc402 100644 +--- a/drivers/net/ixgbe/ixgbe_flow.c ++++ b/drivers/net/ixgbe/ixgbe_flow.c +@@ -2,7 +2,6 @@ + * Copyright(c) 2010-2016 Intel Corporation + */ + +-#include + #include + #include + #include +@@ -15,6 +14,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -468,6 +468,29 @@ const struct rte_flow_action *next_no_void_action( + } + + tcp_spec = item->spec; ++ /* ++ * DPVS filted by fdir is expected, ++ * With dpvs single worker mode pattern had set: ++ * ----------------------------------------------- ++ * ITEM Spec Mask ++ * ETH NULL NULL ++ * IPV4|6 src_addr 0 0 ++ * dst_addr laddr 0xFFFFFFFF ++ * UDP|TCP src_port 0 0 ++ * dst_port 0 0 ++ * END ++ * ----------------------------------------------- ++ * It should return error here ++ * And continue by ixgbe_parse_fdir_filter() ++ * */ ++ if (tcp_spec->hdr.dst_port == 0 && ++ tcp_mask->hdr.dst_port == 0) { ++ memset(filter, 0, sizeof(struct rte_eth_ntuple_filter)); ++ rte_flow_error_set(error, EINVAL, ++ RTE_FLOW_ERROR_TYPE_ITEM, ++ item, "Not supported by ntuple filter"); ++ return -rte_errno; ++ } + filter->dst_port = tcp_spec->hdr.dst_port; + filter->src_port = tcp_spec->hdr.src_port; + filter->tcp_flags = tcp_spec->hdr.tcp_flags; +@@ -501,6 +524,30 @@ const struct rte_flow_action *next_no_void_action( + filter->src_port_mask = udp_mask->hdr.src_port; + + udp_spec = item->spec; ++ /* ++ * DPVS filted by fdir is expected, ++ * With dpvs single worker mode pattern had set: ++ * ----------------------------------------------- ++ * ITEM Spec Mask ++ * ETH NULL NULL ++ * IPV4|6 src_addr 0 0 ++ * dst_addr laddr 0xFFFFFFFF ++ * UDP|TCP src_port 0 0 ++ * dst_port 0 0 ++ * END ++ * ----------------------------------------------- ++ * It should return error here ++ * And continue by ixgbe_parse_fdir_filter() ++ * */ ++ ++ if (udp_spec->hdr.dst_port == 0 && ++ udp_mask->hdr.dst_port == 0) { ++ memset(filter, 0, sizeof(struct rte_eth_ntuple_filter)); ++ rte_flow_error_set(error, EINVAL, ++ RTE_FLOW_ERROR_TYPE_ITEM, ++ item, "Not supported by ntuple filter"); ++ return -rte_errno; ++ } + filter->dst_port = udp_spec->hdr.dst_port; + filter->src_port = udp_spec->hdr.src_port; + } else if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) { +@@ -1419,11 +1466,8 @@ const struct rte_flow_action *next_no_void_action( + + /* not supported */ + if (attr->priority) { +- memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); +- rte_flow_error_set(error, EINVAL, +- RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, +- attr, "Not support priority."); +- return -rte_errno; ++ PMD_DRV_LOG(INFO, "ixgbe flow doesn't support priority %d " ++ "(priority must be 0), ignore and continue....\n", attr->priority); + } + + /* check if the first not void action is QUEUE or DROP. */ +@@ -1642,7 +1686,7 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + * value. So, we need not do anything for the not provided fields later. + */ + memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); +- memset(&rule->mask, 0xFF, sizeof(struct ixgbe_hw_fdir_mask)); ++ memset(&rule->mask, 0, sizeof(struct ixgbe_hw_fdir_mask)); /* mask default zero */ + rule->mask.vlan_tci_mask = 0; + rule->mask.flex_bytes_mask = 0; + +@@ -1760,6 +1804,8 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + } + } else { + if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 && ++ /* Signature mode supports IPv6. */ ++ item->type != RTE_FLOW_ITEM_TYPE_IPV6 && + item->type != RTE_FLOW_ITEM_TYPE_VLAN) { + memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); + rte_flow_error_set(error, EINVAL, +@@ -1815,6 +1861,10 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + */ + rule->ixgbe_fdir.formatted.flow_type = + IXGBE_ATR_FLOW_TYPE_IPV4; ++ ++ /* Update flow rule mode by global param. */ ++ rule->mode = dev->data->dev_conf.fdir_conf.mode; ++ + /*Not supported last point for range*/ + if (item->last) { + rte_flow_error_set(error, EINVAL, +@@ -1888,6 +1938,9 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + rule->ixgbe_fdir.formatted.flow_type = + IXGBE_ATR_FLOW_TYPE_IPV6; + ++ /* Update flow rule mode by global param. */ ++ rule->mode = dev->data->dev_conf.fdir_conf.mode; ++ + /** + * 1. must signature match + * 2. not support last +@@ -2748,12 +2801,45 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + return ixgbe_parse_fdir_act_attr(attr, actions, rule, error); + } + ++static inline int ++ixgbe_fdir_rule_patch(struct rte_eth_dev *dev, struct ixgbe_fdir_rule *rule) ++{ ++ static uint32_t softid[IXGBE_MAX_RX_QUEUE_NUM] = { 0 }; ++ ++ if (!rule) ++ return 0; ++ ++ if (!dev || !dev->data) ++ return -EINVAL; ++ if (rule->queue >= IXGBE_MAX_RX_QUEUE_NUM) ++ return -EINVAL; ++ ++ /* Soft-id for different rx-queue should be different. */ ++ rule->soft_id = softid[rule->queue]++; ++ ++ /* Disable mask config from rte_flow. ++ * FIXME: ++ * Ixgbe only supports one global mask, all the masks should be the same. ++ * Generally, fdir masks should be configured globally before port start. ++ * But the rte_flow configures masks at flow creation. So we disable fdir ++ * mask configs in rte_flow and configure it globally when port start. ++ * Refer to `ixgbe_dev_start/ixgbe_fdir_configure` for details. The global ++ * masks are configured into device initially with user specified params. ++ */ ++ rule->b_mask = 0; ++ ++ /* Use user-defined mode. */ ++ rule->mode = dev->data->dev_conf.fdir_conf.mode; ++ ++ return 0; ++} ++ + static int + ixgbe_parse_fdir_filter(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], +- struct ixgbe_fdir_rule *rule, ++ struct ixgbe_fdir_rule *rule, bool b_patch, + struct rte_flow_error *error) + { + int ret; +@@ -2787,13 +2873,18 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + rule->ixgbe_fdir.formatted.dst_port != 0)) + return -ENOTSUP; + +- if (fdir_mode == RTE_FDIR_MODE_NONE || +- fdir_mode != rule->mode) ++ if (fdir_mode == RTE_FDIR_MODE_NONE) + return -ENOTSUP; + + if (rule->queue >= dev->data->nb_rx_queues) + return -ENOTSUP; + ++ if (ret) ++ return ret; ++ ++ if (b_patch) ++ return ixgbe_fdir_rule_patch(dev, rule); ++ + return ret; + } + +@@ -3128,7 +3219,7 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + + memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule)); + ret = ixgbe_parse_fdir_filter(dev, attr, pattern, +- actions, &fdir_rule, error); ++ actions, &fdir_rule, true, error); + if (!ret) { + /* A mask cannot be deleted. */ + if (fdir_rule.b_mask) { +@@ -3299,7 +3390,7 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + + memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule)); + ret = ixgbe_parse_fdir_filter(dev, attr, pattern, +- actions, &fdir_rule, error); ++ actions, &fdir_rule, false, error); + if (!ret) + return 0; + +@@ -3335,7 +3426,7 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + struct ixgbe_eth_syn_filter_ele *syn_filter_ptr; + struct ixgbe_eth_l2_tunnel_conf_ele *l2_tn_filter_ptr; + struct ixgbe_fdir_rule_ele *fdir_rule_ptr; +- struct ixgbe_flow_mem *ixgbe_flow_mem_ptr; ++ struct ixgbe_flow_mem *ixgbe_flow_mem_ptr, *next_ptr; + struct ixgbe_hw_fdir_info *fdir_info = + IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private); + struct ixgbe_rss_conf_ele *rss_filter_ptr; +@@ -3432,7 +3523,7 @@ static inline uint8_t signature_match(const struct rte_flow_item pattern[]) + return ret; + } + +- TAILQ_FOREACH(ixgbe_flow_mem_ptr, &ixgbe_flow_list, entries) { ++ TAILQ_FOREACH_SAFE(ixgbe_flow_mem_ptr, &ixgbe_flow_list, entries, next_ptr) { + if (ixgbe_flow_mem_ptr->flow == pmd_flow) { + TAILQ_REMOVE(&ixgbe_flow_list, + ixgbe_flow_mem_ptr, entries); +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0005-bonding-allow-slaves-from-different-numa-nodes.patch b/patch/dpdk-stable-20.11.10/0005-bonding-allow-slaves-from-different-numa-nodes.patch new file mode 100644 index 000000000..12a011e16 --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0005-bonding-allow-slaves-from-different-numa-nodes.patch @@ -0,0 +1,50 @@ +From 30c3918317ea30a7586f2c081a6623c4574dade9 Mon Sep 17 00:00:00 2001 +From: huangyichen +Date: Wed, 4 Aug 2021 15:16:04 +0800 +Subject: [PATCH 5/7] bonding: allow slaves from different numa nodes + +Note the patch may have a negative influnce on performance. +It's not a good practice to bonding slaves across numa nodes. +--- + drivers/net/bonding/rte_eth_bond_pmd.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c +index 0c9a1df..371c888 100644 +--- a/drivers/net/bonding/rte_eth_bond_pmd.c ++++ b/drivers/net/bonding/rte_eth_bond_pmd.c +@@ -1780,7 +1780,14 @@ struct bwg_slave { + + errval = rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, +- rte_eth_dev_socket_id(slave_eth_dev->data->port_id), ++ // In spite of performance problem, bonding slaves had better to support ++ // slaves from different numa nodes. Considering that numa node on which ++ // the resources of bonding port is allocated from is specified by ++ // rte_eth_bond_create() at bonding creation, the slave's queue_setup ++ // would fail if specified with the slave's numa node id that is different ++ // from the one of the bonding port. See rte_eth_dma_zone_reserve() for ++ // details. ++ SOCKET_ID_ANY, + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool); + if (errval != 0) { + RTE_BOND_LOG(ERR, +@@ -1796,7 +1803,14 @@ struct bwg_slave { + + errval = rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, +- rte_eth_dev_socket_id(slave_eth_dev->data->port_id), ++ // In spite of performance problem, bonding slaves had better to support ++ // slaves from different numa nodes. Considering that numa node on which ++ // the resources of bonding port is allocated from is specified by ++ // rte_eth_bond_create() at bonding creation, the slave's queue_setup ++ // would fail if specified with the slave's numa node id that is different ++ // from the one of the bonding port. See rte_eth_dma_zone_reserve() for ++ // details. ++ SOCKET_ID_ANY, + &bd_tx_q->tx_conf); + if (errval != 0) { + RTE_BOND_LOG(ERR, +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0006-bonding-fix-problem-in-mode-4-dropping-multicast-pac.patch b/patch/dpdk-stable-20.11.10/0006-bonding-fix-problem-in-mode-4-dropping-multicast-pac.patch new file mode 100644 index 000000000..c7f420f8d --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0006-bonding-fix-problem-in-mode-4-dropping-multicast-pac.patch @@ -0,0 +1,79 @@ +From 2d3c711e48d4f09200096348be1286eec10301f6 Mon Sep 17 00:00:00 2001 +From: yuwenchao +Date: Fri, 2 Aug 2024 13:32:36 +0800 +Subject: [PATCH 6/7] bonding: fix problem in mode 4 dropping multicast packets + +Signed-off-by: yuwenchao +--- + drivers/net/bonding/rte_eth_bond_pmd.c | 38 +++++++++++++++++++++------------- + 1 file changed, 24 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c +index 371c888..f770f50 100644 +--- a/drivers/net/bonding/rte_eth_bond_pmd.c ++++ b/drivers/net/bonding/rte_eth_bond_pmd.c +@@ -309,7 +309,6 @@ + + uint8_t collecting; /* current slave collecting status */ + const uint8_t promisc = rte_eth_promiscuous_get(internals->port_id); +- const uint8_t allmulti = rte_eth_allmulticast_get(internals->port_id); + uint8_t subtype; + uint16_t i; + uint16_t j; +@@ -352,20 +351,28 @@ + * - bonding interface is not in promiscuous mode and + * packet address isn't in mac_addrs array: + * - packet is unicast, +- * - packet is multicast and bonding interface +- * is not in allmulti, ++ * ++ * Notes: ++ * Multicast packets, such as OSPF protocol packets, should not ++ * be dropped, instead they must deliver to DPVS application. + */ +- if (unlikely( +- (!dedicated_rxq && +- is_lacp_packets(hdr->ether_type, subtype, +- bufs[j])) || +- !collecting || +- (!promisc && +- !is_bond_mac_addr(&hdr->d_addr, bond_mac, +- BOND_MAX_MAC_ADDRS) && +- (rte_is_unicast_ether_addr(&hdr->d_addr) || +- !allmulti)))) { ++ if (unlikely((is_lacp_packets(hdr->ether_type, subtype, bufs[j])) ++ || !collecting ++ || (!promisc && !is_bond_mac_addr(&hdr->d_addr, bond_mac, ++ BOND_MAX_MAC_ADDRS) ++ && (rte_is_unicast_ether_addr(&hdr->d_addr))))) { + if (hdr->ether_type == ether_type_slow_be) { ++ if (dedicated_rxq) { ++ /* Error! Lacp packets should never appear here if ++ * dedicated queue enabled. This can be caused by ++ * a lack of support for ethertype rte_flow. Just ++ * issue a warning rather than dropping the packets ++ * so that the lacp state machine can work properly. ++ * */ ++ RTE_BOND_LOG(WARNING, "receive lacp packets from queue %d " ++ "of port %d when dedicated queue enabled", ++ bd_rx_q->queue_id, slaves[idx]); ++ } + bond_mode_8023ad_handle_slow_pkt( + internals, slaves[idx], bufs[j]); + } else +@@ -1288,8 +1295,11 @@ struct bwg_slave { + slave_port_ids[i]; + } + +- if (unlikely(dist_slave_count < 1)) ++ if (unlikely(dist_slave_count < 1)) { ++ RTE_BOND_LOG(WARNING, "no distributing slaves on bonding port %d", ++ internals->port_id); + return 0; ++ } + + return tx_burst_balance(queue, bufs, nb_bufs, dist_slave_port_ids, + dist_slave_count); +-- +1.8.3.1 + diff --git a/patch/dpdk-stable-20.11.10/0007-bonding-device-sends-packets-with-user-specified-sal.patch b/patch/dpdk-stable-20.11.10/0007-bonding-device-sends-packets-with-user-specified-sal.patch new file mode 100644 index 000000000..50b5ebf82 --- /dev/null +++ b/patch/dpdk-stable-20.11.10/0007-bonding-device-sends-packets-with-user-specified-sal.patch @@ -0,0 +1,92 @@ +From 69849e246e15f3e202e539809cefd18fe7083575 Mon Sep 17 00:00:00 2001 +From: yuwenchao +Date: Tue, 30 Jul 2024 15:39:28 +0800 +Subject: [PATCH 7/7] bonding device sends packets with user specified salve + port + +The outgoing slave port is specified in mbuf field "hash.txadapter.reserved2". +Support the following 3 bonding mode: +- mode 0: round robin +- mode 2: balance +- mode 4: 8023ad + +Signed-off-by: yuwenchao +--- + drivers/net/bonding/rte_eth_bond_pmd.c | 26 ++++++++++++++++++++++++-- + lib/librte_mbuf/rte_mbuf.h | 2 ++ + 2 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c +index f770f50..3c93365 100644 +--- a/drivers/net/bonding/rte_eth_bond_pmd.c ++++ b/drivers/net/bonding/rte_eth_bond_pmd.c +@@ -587,6 +587,22 @@ struct client_stats_t { + return nb_recv_pkts; + } + ++static inline int ++bond_ethdev_populate_slave_by_user(const struct rte_mbuf *mbuf, const uint16_t *slaves, ++ int num_slave) ++{ ++ uint16_t i, pid = mbuf->hash.txadapter.reserved2; ++ ++ if (likely(pid == RTE_MBUF_PORT_INVALID)) ++ return -1; ++ ++ for (i = 0; i < num_slave; i++) { ++ if (slaves[i] == pid) ++ return i; ++ } ++ return -1; ++} ++ + static uint16_t + bond_ethdev_tx_burst_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +@@ -619,7 +635,9 @@ struct client_stats_t { + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { +- cslave_idx = (slave_idx + i) % num_of_slaves; ++ cslave_idx = bond_ethdev_populate_slave_by_user(bufs[i], slaves, num_of_slaves); ++ if (likely(cslave_idx < 0)) ++ cslave_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cslave_idx][(slave_nb_pkts[cslave_idx])++] = bufs[i]; + } + +@@ -1176,7 +1194,11 @@ struct bwg_slave { + + for (i = 0; i < nb_bufs; i++) { + /* Populate slave mbuf arrays with mbufs for that slave. */ +- uint16_t slave_idx = bufs_slave_port_idxs[i]; ++ int slave_idx; ++ ++ slave_idx = bond_ethdev_populate_slave_by_user(bufs[i], slave_port_ids, slave_count); ++ if (likely(slave_idx < 0)) ++ slave_idx = bufs_slave_port_idxs[i]; + + slave_bufs[slave_idx][slave_nb_bufs[slave_idx]++] = bufs[i]; + } +diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h +index d079462..d6072ea 100644 +--- a/lib/librte_mbuf/rte_mbuf.h ++++ b/lib/librte_mbuf/rte_mbuf.h +@@ -589,6 +589,7 @@ static inline struct rte_mbuf *rte_mbuf_raw_alloc(struct rte_mempool *mp) + + if (rte_mempool_get(mp, (void **)&m) < 0) + return NULL; ++ m->hash.txadapter.reserved2 = RTE_MBUF_PORT_INVALID; + __rte_mbuf_raw_sanity_check(m); + return m; + } +@@ -864,6 +865,7 @@ static inline void rte_pktmbuf_reset(struct rte_mbuf *m) + m->vlan_tci_outer = 0; + m->nb_segs = 1; + m->port = RTE_MBUF_PORT_INVALID; ++ m->hash.txadapter.reserved2 = RTE_MBUF_PORT_INVALID; + + m->ol_flags &= EXT_ATTACHED_MBUF; + m->packet_type = 0; +-- +1.8.3.1 + diff --git a/patch/nginx/README.md b/patch/nginx/README.md new file mode 100644 index 000000000..2614a3f5a --- /dev/null +++ b/patch/nginx/README.md @@ -0,0 +1,80 @@ +Nginx Patches for DPVS +----- + +The directory is arranged to place nginx patch files for DPVS. More specifically, it contains the following patches. + +* TOA patch for originating client IP/port derived from DPVS NAT64 translation +* UOA patch for originating client IP/port derived from DPVS UDP FNAT/NAT64 translation in QUIC/HTTP3 +* QUIC Server Connection ID patch for connection migration + +## TOA NAT64 + +Nginx can get the originating client IP address and Port NAT64'ed by DPVS by utilizing nginx variables 'toa_remote_addr' and 'toa_remote_port' respectively. It works when and only when the TOA kernel module has already installed successfully on the nginx server. + +This is an exampe configuration of nginx with TOA patch for NAT64. + +``` +http { + include mime.types; + default_type application/octet-stream; + + log_format nat64 '$remote_addr $toa_remote_addr :$toa_remote_port - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" ' + '$request_length $upstream_response_time $upstream_addr'; + + access_log logs/access.log nat64; + + # more other configs ...... + +} +``` + +## UOA QUIC/HTTP3 + +Nginx can get the originating client IP address and Port NAT'ed by DPVS by utilizing nginx variables 'uoa_remote_addr' and 'uoa_remote_port' respectively. Both IPv4-IPv4 and IPv6-IPv6 NAT and NAT64(IPv6-IPv4 NAT) as well are supported. It works when and only when the UOA kernel module has already installed sucessfully on the nginx server. + +This is an exampe configuration of nginx with UOA patch. + +``` +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http3" ' + '"$http_x_forwarded_for" $request_length $upstream_response_time $upstream_addr'; + + log_format quic '$remote_addr $uoa_remote_addr :$uoa_remote_port - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http3" ' + '"$http_x_forwarded_for" $request_length $upstream_response_time $upstream_addr'; + + access_log logs/access.log main; + + # more other configs ...... + + + server { + listen 443 quic reuseport; + listen 443 ssl; + + server_name qlb-test.qiyi.domain; + + access_log logs/quic.access.log quic; + + ssl_certificate certs/cert.pem; + ssl_certificate_key certs/key.pem; + + location / { + add_header Alt-Svc 'h3=":2443"; ma=86400'; + root html; + index index.html index.htm; + } + } +} +``` + +## Quic Server Connection ID + +It requires changes to Quic Server Connection ID(SCID) both in DPVS and Nginx to support the feature of QUIC connection migration. DPVS depends on Server IP/Port information encoded in SCID to schedule a migrating connection to the right nginx server where the previous connection resides, and Nginx relies on the socket cookie compiled in SCID to make a migrating connection be processed on the same listening socket as the previous one. Note that eBPF (bpf_sk_select_reuseport) is used in Nginx for QUIC connection migration, which requires Linux 5.7+. + +The patch adds Nginx server address information into SCID, and fixes its collision problem with Nginx's socket cookie. The server address contains 24 least significant bits(LSB) for IPv4, and 32 LSB for IPv6, and compliant with DPVS DCID format specification defined in [ipvs/quic.h](../../include/ipvs/quic.h). The server port is not included in SCID. diff --git a/kmod/toa/example_nat64/nginx/nginx-1.14.0-nat64-toa.patch b/patch/nginx/v1.14.0/nginx-1.14.0-nat64-toa.patch similarity index 100% rename from kmod/toa/example_nat64/nginx/nginx-1.14.0-nat64-toa.patch rename to patch/nginx/v1.14.0/nginx-1.14.0-nat64-toa.patch diff --git a/patch/nginx/v1.26.0/0001-patch-toa-for-nat64-and-uoa-for-quic.patch b/patch/nginx/v1.26.0/0001-patch-toa-for-nat64-and-uoa-for-quic.patch new file mode 100644 index 000000000..8f9838523 --- /dev/null +++ b/patch/nginx/v1.26.0/0001-patch-toa-for-nat64-and-uoa-for-quic.patch @@ -0,0 +1,398 @@ +From b5137f7826d2fa85352cbb1afe5f2317a4f3abd1 Mon Sep 17 00:00:00 2001 +From: wencyu +Date: Fri, 24 May 2024 16:37:06 +0800 +Subject: [PATCH 1/7] patch: toa for nat64 and uoa for quic + +Signed-off-by: wencyu +--- + src/core/ngx_connection.h | 5 ++ + src/core/ngx_inet.h | 44 +++++++++++ + src/event/ngx_event_accept.c | 23 +++++- + src/event/quic/ngx_event_quic_streams.c | 15 +++- + src/event/quic/ngx_event_quic_udp.c | 38 ++++++++- + src/http/ngx_http_variables.c | 133 ++++++++++++++++++++++++++++++++ + 6 files changed, 255 insertions(+), 3 deletions(-) + +diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h +index 84dd804..0ce4c69 100644 +--- a/src/core/ngx_connection.h ++++ b/src/core/ngx_connection.h +@@ -147,6 +147,11 @@ struct ngx_connection_s { + socklen_t socklen; + ngx_str_t addr_text; + ++ union { ++ struct sockaddr *toa_addr; ++ struct sockaddr *uoa_addr; ++ }; ++ + ngx_proxy_protocol_t *proxy_protocol; + + #if (NGX_QUIC || NGX_COMPAT) +diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h +index 19050fc..3365c8e 100644 +--- a/src/core/ngx_inet.h ++++ b/src/core/ngx_inet.h +@@ -106,6 +106,50 @@ typedef struct { + } ngx_url_t; + + ++/* toa socket options, now only for nat64 */ ++enum { ++ TOA_BASE_CTL = 4096, ++ /* set */ ++ TOA_SO_SET_MAX = TOA_BASE_CTL, ++ /* get */ ++ TOA_SO_GET_LOOKUP = TOA_BASE_CTL, ++ TOA_SO_GET_MAX = TOA_SO_GET_LOOKUP, ++}; ++ ++/* uoa socket options */ ++enum { ++ UOA_BASE_CTL = 2048, ++ /* set */ ++ UOA_SO_SET_MAX = UOA_BASE_CTL, ++ /* get */ ++ UOA_SO_GET_LOOKUP = UOA_BASE_CTL, ++ UOA_SO_GET_MAX = UOA_SO_GET_LOOKUP, ++}; ++ ++typedef struct { ++ struct in6_addr saddr; ++ uint16_t port; ++} toa_nat64_peer_t; ++ ++typedef union { ++ struct in_addr in; ++ struct in6_addr in6; ++} uoa_addr_t; ++ ++typedef struct { ++ /* input */ ++ uint16_t af; ++ uoa_addr_t saddr; ++ uoa_addr_t daddr; ++ uint16_t sport; ++ uint16_t dport; ++ /* output */ ++ uint16_t real_af; ++ uoa_addr_t real_saddr; ++ uint16_t real_sport; ++} __attribute__((__packed__)) uoa_param_map_t; ++ ++ + in_addr_t ngx_inet_addr(u_char *text, size_t len); + #if (NGX_HAVE_INET6) + ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr); +diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c +index 2703879..66f28b2 100644 +--- a/src/event/ngx_event_accept.c ++++ b/src/event/ngx_event_accept.c +@@ -20,7 +20,7 @@ static void ngx_close_accepted_connection(ngx_connection_t *c); + void + ngx_event_accept(ngx_event_t *ev) + { +- socklen_t socklen; ++ socklen_t socklen, toa64_addrlen; + ngx_err_t err; + ngx_log_t *log; + ngx_uint_t level; +@@ -30,6 +30,7 @@ ngx_event_accept(ngx_event_t *ev) + ngx_listening_t *ls; + ngx_connection_t *c, *lc; + ngx_event_conf_t *ecf; ++ toa_nat64_peer_t toa64_addr; + #if (NGX_HAVE_ACCEPT4) + static ngx_uint_t use_accept4 = 1; + #endif +@@ -174,6 +175,26 @@ ngx_event_accept(ngx_event_t *ev) + + ngx_memcpy(c->sockaddr, &sa, socklen); + ++ ++ /* get nat64 toa remote addr & port */ ++ toa64_addrlen = sizeof(toa_nat64_peer_t); ++ if (getsockopt(s, IPPROTO_IP, TOA_SO_GET_LOOKUP, &toa64_addr, ++ &toa64_addrlen) == NGX_OK) { ++ struct sockaddr_in6 *toa64_sa; ++ c->toa_addr = ngx_palloc(c->pool, sizeof(struct sockaddr_in6)); ++ if (c->toa_addr == NULL) { ++ ngx_close_accepted_connection(c); ++ return; ++ } ++ ngx_memzero(c->toa_addr, sizeof(struct sockaddr_in6)); ++ toa64_sa = (struct sockaddr_in6 *)c->toa_addr; ++ toa64_sa->sin6_family = AF_INET6; ++ toa64_sa->sin6_addr = toa64_addr.saddr; ++ toa64_sa->sin6_port = toa64_addr.port; ++ } else { ++ c->toa_addr = NULL; ++ } ++ + log = ngx_palloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + ngx_close_accepted_connection(c); +diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c +index 178b805..ce11cff 100644 +--- a/src/event/quic/ngx_event_quic_streams.c ++++ b/src/event/quic/ngx_event_quic_streams.c +@@ -646,7 +646,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) + ngx_pool_t *pool; + ngx_uint_t reusable; + ngx_queue_t *q; +- struct sockaddr *sockaddr; ++ struct sockaddr *sockaddr, *uoa_addr; + ngx_connection_t *sc; + ngx_quic_stream_t *qs; + ngx_pool_cleanup_t *cln; +@@ -707,6 +707,18 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) + + ngx_memcpy(sockaddr, c->sockaddr, c->socklen); + ++ if (c->uoa_addr) { ++ uoa_addr = ngx_palloc(pool, sizeof(struct sockaddr_storage)); ++ if (uoa_addr == NULL) { ++ ngx_destroy_pool(pool); ++ ngx_queue_insert_tail(&qc->streams.free, &qs->queue); ++ return NULL; ++ } ++ ngx_memcpy(uoa_addr, c->uoa_addr, sizeof(struct sockaddr_storage)); ++ } else { ++ uoa_addr = NULL; ++ } ++ + if (c->addr_text.data) { + addr_text.data = ngx_pnalloc(pool, c->addr_text.len); + if (addr_text.data == NULL) { +@@ -743,6 +755,7 @@ ngx_quic_create_stream(ngx_connection_t *c, uint64_t id) + sc->ssl = c->ssl; + sc->sockaddr = sockaddr; + sc->socklen = c->socklen; ++ sc->uoa_addr = uoa_addr; + sc->listening = c->listening; + sc->addr_text = addr_text; + sc->local_sockaddr = c->local_sockaddr; +diff --git a/src/event/quic/ngx_event_quic_udp.c b/src/event/quic/ngx_event_quic_udp.c +index 15b54bc..e94e0f5 100644 +--- a/src/event/quic/ngx_event_quic_udp.c ++++ b/src/event/quic/ngx_event_quic_udp.c +@@ -24,7 +24,7 @@ ngx_quic_recvmsg(ngx_event_t *ev) + ngx_buf_t buf; + ngx_log_t *log; + ngx_err_t err; +- socklen_t socklen, local_socklen; ++ socklen_t socklen, local_socklen, uoamap_len; + ngx_event_t *rev, *wev; + struct iovec iov[1]; + struct msghdr msg; +@@ -34,6 +34,7 @@ ngx_quic_recvmsg(ngx_event_t *ev) + ngx_event_conf_t *ecf; + ngx_connection_t *c, *lc; + ngx_quic_socket_t *qsock; ++ uoa_param_map_t uoamap; + static u_char buffer[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; + + #if (NGX_HAVE_ADDRINFO_CMSG) +@@ -238,6 +239,41 @@ ngx_quic_recvmsg(ngx_event_t *ev) + + ngx_memcpy(c->sockaddr, sockaddr, socklen); + ++ /* parse uoa address */ ++ if (sockaddr->sa_family == AF_INET || sockaddr->sa_family == AF_INET6) { ++ uoamap_len = sizeof(uoamap); ++ ngx_memzero(&uoamap, uoamap_len); ++ uoamap.af = sockaddr->sa_family; ++ if (uoamap.af == AF_INET) { ++ uoamap.saddr.in = ((ngx_sockaddr_t *)sockaddr)->sockaddr_in.sin_addr; ++ uoamap.sport = ((ngx_sockaddr_t *)sockaddr)->sockaddr_in.sin_port; ++ uoamap.dport = ((ngx_sockaddr_t *)local_sockaddr)->sockaddr_in.sin_port; ++ } else { ++ uoamap.saddr.in6 = ((ngx_sockaddr_t *)sockaddr)->sockaddr_in6.sin6_addr; ++ uoamap.sport = ((ngx_sockaddr_t *)sockaddr)->sockaddr_in6.sin6_port; ++ uoamap.dport = ((ngx_sockaddr_t *)local_sockaddr)->sockaddr_in6.sin6_port; ++ } ++ if (getsockopt(lc->fd, IPPROTO_IP, UOA_SO_GET_LOOKUP, &uoamap, &uoamap_len) ++ == NGX_OK) { ++ c->uoa_addr = ngx_palloc(c->pool, sizeof(struct sockaddr_storage)); ++ if (c->uoa_addr == NULL) { ++ ngx_quic_close_accepted_connection(c); ++ return; ++ } ++ ngx_memzero(c->uoa_addr, sizeof(struct sockaddr_storage)); ++ c->uoa_addr->sa_family = uoamap.real_af; ++ if (uoamap.real_af == AF_INET) { ++ ((ngx_sockaddr_t *)c->uoa_addr)->sockaddr_in.sin_port = uoamap.real_sport; ++ ((ngx_sockaddr_t *)c->uoa_addr)->sockaddr_in.sin_addr = uoamap.real_saddr.in; ++ } else { ++ ((ngx_sockaddr_t *)c->uoa_addr)->sockaddr_in6.sin6_port = uoamap.real_sport; ++ ((ngx_sockaddr_t *)c->uoa_addr)->sockaddr_in6.sin6_addr = uoamap.real_saddr.in6; ++ } ++ } else { ++ c->uoa_addr = NULL; ++ } ++ } ++ + log = ngx_palloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + ngx_quic_close_accepted_connection(c); +diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c +index 4f0bd0e..55c6bd7 100644 +--- a/src/http/ngx_http_variables.c ++++ b/src/http/ngx_http_variables.c +@@ -57,6 +57,14 @@ static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); ++static ngx_int_t ngx_http_variable_toa_remote_addr(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++static ngx_int_t ngx_http_variable_toa_remote_port(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++static ngx_int_t ngx_http_variable_uoa_remote_addr(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++static ngx_int_t ngx_http_variable_uoa_remote_port(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); + static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, +@@ -200,6 +208,14 @@ static ngx_http_variable_t ngx_http_core_variables[] = { + + { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 }, + ++ { ngx_string("toa_remote_addr"), NULL, ngx_http_variable_toa_remote_addr, 0, 0, 0 }, ++ ++ { ngx_string("toa_remote_port"), NULL, ngx_http_variable_toa_remote_port, 0, 0, 0 }, ++ ++ { ngx_string("uoa_remote_addr"), NULL, ngx_http_variable_uoa_remote_addr, 0, 0, 0 }, ++ ++ { ngx_string("uoa_remote_port"), NULL, ngx_http_variable_uoa_remote_port, 0, 0, 0 }, ++ + { ngx_string("proxy_protocol_addr"), NULL, + ngx_http_variable_proxy_protocol_addr, + offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 }, +@@ -1335,6 +1351,123 @@ ngx_http_variable_remote_port(ngx_http_request_t *r, + return NGX_OK; + } + ++static ngx_int_t ngx_http_variable_toa_remote_addr(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) { ++ struct sockaddr_in6 *sa6; ++ size_t len; ++ ++ len = r->connection->toa_addr ? NGX_INET6_ADDRSTRLEN : 1; ++ v->data = ngx_pnalloc(r->pool, len); ++ if (v->data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = 0; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ if (r->connection->toa_addr) { ++ sa6 = (struct sockaddr_in6 *)r->connection->toa_addr; ++ v->len = ngx_inet_ntop(sa6->sin6_family, &sa6->sin6_addr, (u_char *)v->data, len); ++ } else { ++ v->data[0] = '-'; ++ v->len = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_http_variable_toa_remote_port(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) { ++ ngx_uint_t port; ++ size_t len; ++ ++ len = r->connection->toa_addr ? sizeof("65535") - 1 : 1; ++ v->data = ngx_pnalloc(r->pool, len); ++ if (v->data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = 0; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ if (r->connection->toa_addr) { ++ port = ngx_inet_get_port(r->connection->toa_addr); ++ if (port > 0 && port < 65536) { ++ v->len = ngx_sprintf(v->data, "%ui", port) - v->data; ++ } ++ } else { ++ v->data[0] = '-'; ++ v->len = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_http_variable_uoa_remote_addr(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) { ++ struct sockaddr_in *sa; ++ struct sockaddr_in6 *sa6; ++ size_t len; ++ ++ len = r->connection->uoa_addr ? NGX_INET6_ADDRSTRLEN : 1; ++ v->data = ngx_pnalloc(r->pool, len); ++ if (v->data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = 0; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ if (r->connection->uoa_addr) { ++ if (r->connection->uoa_addr->sa_family == AF_INET6) { ++ sa6 = (struct sockaddr_in6 *)r->connection->uoa_addr; ++ v->len = ngx_inet_ntop(sa6->sin6_family, &sa6->sin6_addr, (u_char *)v->data, len); ++ } else { ++ sa = (struct sockaddr_in *)r->connection->uoa_addr; ++ v->len = ngx_inet_ntop(sa->sin_family, &sa->sin_addr, (u_char *)v->data, len); ++ } ++ } else { ++ v->data[0] = '-'; ++ v->len = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_http_variable_uoa_remote_port(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) { ++ ngx_uint_t port; ++ size_t len; ++ ++ len = r->connection->uoa_addr ? sizeof("65535") - 1 : 1; ++ v->data = ngx_pnalloc(r->pool, len); ++ if (v->data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = 0; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ if (r->connection->uoa_addr) { ++ port = ngx_inet_get_port(r->connection->uoa_addr); ++ if (port > 0 && port < 65536) { ++ v->len = ngx_sprintf(v->data, "%ui", port) - v->data; ++ } ++ } else { ++ v->data[0] = '-'; ++ v->len = 1; ++ } ++ ++ return NGX_OK; ++} + + static ngx_int_t + ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, +-- +1.8.3.1 + diff --git a/patch/nginx/v1.26.0/0002-patch-quic-connection-migration-for-l4lb-dpvs.patch b/patch/nginx/v1.26.0/0002-patch-quic-connection-migration-for-l4lb-dpvs.patch new file mode 100644 index 000000000..52962a860 --- /dev/null +++ b/patch/nginx/v1.26.0/0002-patch-quic-connection-migration-for-l4lb-dpvs.patch @@ -0,0 +1,248 @@ +From 832740515032f6169635f13eacc0fa50d5560d51 Mon Sep 17 00:00:00 2001 +From: wencyu +Date: Wed, 29 May 2024 10:20:09 +0800 +Subject: [PATCH 2/7] patch: quic connection migration for l4lb/dpvs + +Signed-off-by: wencyu +--- + src/event/quic/bpf/ngx_quic_reuseport_helper.c | 8 ++-- + src/event/quic/ngx_event_quic_bpf_code.c | 58 +++++++++++++----------- + src/event/quic/ngx_event_quic_connid.c | 62 ++++++++++++++++++++++++++ + src/event/quic/ngx_event_quic_transport.c | 1 + + 4 files changed, 101 insertions(+), 28 deletions(-) + +diff --git a/src/event/quic/bpf/ngx_quic_reuseport_helper.c b/src/event/quic/bpf/ngx_quic_reuseport_helper.c +index 999e760..bdca492 100644 +--- a/src/event/quic/bpf/ngx_quic_reuseport_helper.c ++++ b/src/event/quic/bpf/ngx_quic_reuseport_helper.c +@@ -76,7 +76,7 @@ int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx) + int rc; + __u64 key; + size_t len, offset; +- unsigned char *start, *end, *data, *dcid; ++ unsigned char *start, *end, *data, *dcid, *cookie; + + start = ctx->data; + end = (unsigned char *) ctx->data_end; +@@ -104,12 +104,14 @@ int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx) + dcid = &data[1]; + advance_data(len); /* we expect the packet to have full DCID */ + ++ cookie = dcid + (len - sizeof(__u64)); /* socket cookie is at the tail of DCID */ ++ + /* make verifier happy */ +- if (dcid + sizeof(__u64) > end) { ++ if (cookie + sizeof(__u64) > end) { + goto failed; + } + +- key = ngx_quic_parse_uint64(dcid); ++ key = ngx_quic_parse_uint64(cookie); + + rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0); + +diff --git a/src/event/quic/ngx_event_quic_bpf_code.c b/src/event/quic/ngx_event_quic_bpf_code.c +index 5c9dea1..1124c04 100644 +--- a/src/event/quic/ngx_event_quic_bpf_code.c ++++ b/src/event/quic/ngx_event_quic_bpf_code.c +@@ -7,62 +7,69 @@ + + + static ngx_bpf_reloc_t bpf_reloc_prog_ngx_quic_reuseport_helper[] = { +- { "ngx_quic_sockmap", 55 }, ++ { "ngx_quic_sockmap", 62 }, + }; + + static struct bpf_insn bpf_insn_prog_ngx_quic_reuseport_helper[] = { + /* opcode dst src offset imm */ + { 0x79, BPF_REG_4, BPF_REG_1, (int16_t) 0, 0x0 }, + { 0x79, BPF_REG_3, BPF_REG_1, (int16_t) 8, 0x0 }, ++ { 0xbf, BPF_REG_6, BPF_REG_4, (int16_t) 0, 0x0 }, ++ { 0x7, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x8 }, ++ { 0x2d, BPF_REG_6, BPF_REG_3, (int16_t) 61, 0x0 }, + { 0xbf, BPF_REG_2, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0x8 }, +- { 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 54, 0x0 }, +- { 0xbf, BPF_REG_5, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x9 }, +- { 0x2d, BPF_REG_5, BPF_REG_3, (int16_t) 51, 0x0 }, +- { 0xb7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x14 }, ++ { 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0x9 }, ++ { 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 58, 0x0 }, ++ { 0xb7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0x14 }, ++ { 0xb7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x8 }, + { 0xb7, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0x9 }, +- { 0x71, BPF_REG_6, BPF_REG_2, (int16_t) 0, 0x0 }, ++ { 0x71, BPF_REG_6, BPF_REG_6, (int16_t) 0, 0x0 }, + { 0x67, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x38 }, + { 0xc7, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x38 }, +- { 0x65, BPF_REG_6, BPF_REG_0, (int16_t) 10, 0xffffffff }, ++ { 0x65, BPF_REG_6, BPF_REG_0, (int16_t) 11, 0xffffffff }, + { 0xbf, BPF_REG_2, BPF_REG_4, (int16_t) 0, 0x0 }, + { 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0xd }, +- { 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 42, 0x0 }, ++ { 0x2d, BPF_REG_2, BPF_REG_3, (int16_t) 48, 0x0 }, + { 0xbf, BPF_REG_5, BPF_REG_4, (int16_t) 0, 0x0 }, + { 0x7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0xe }, +- { 0x2d, BPF_REG_5, BPF_REG_3, (int16_t) 39, 0x0 }, ++ { 0x2d, BPF_REG_5, BPF_REG_3, (int16_t) 45, 0x0 }, ++ { 0xb7, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0xd }, + { 0xb7, BPF_REG_0, BPF_REG_0, (int16_t) 0, 0xe }, +- { 0x71, BPF_REG_5, BPF_REG_2, (int16_t) 0, 0x0 }, ++ { 0x71, BPF_REG_2, BPF_REG_2, (int16_t) 0, 0x0 }, + { 0xb7, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x8 }, +- { 0x2d, BPF_REG_6, BPF_REG_5, (int16_t) 35, 0x0 }, +- { 0xf, BPF_REG_5, BPF_REG_0, (int16_t) 0, 0x0 }, ++ { 0x2d, BPF_REG_6, BPF_REG_2, (int16_t) 40, 0x0 }, ++ { 0xbf, BPF_REG_6, BPF_REG_2, (int16_t) 0, 0x0 }, ++ { 0xf, BPF_REG_6, BPF_REG_0, (int16_t) 0, 0x0 }, ++ { 0xbf, BPF_REG_0, BPF_REG_4, (int16_t) 0, 0x0 }, ++ { 0xf, BPF_REG_0, BPF_REG_6, (int16_t) 0, 0x0 }, ++ { 0x2d, BPF_REG_0, BPF_REG_3, (int16_t) 35, 0x0 }, + { 0xf, BPF_REG_4, BPF_REG_5, (int16_t) 0, 0x0 }, +- { 0x2d, BPF_REG_4, BPF_REG_3, (int16_t) 32, 0x0 }, ++ { 0xf, BPF_REG_2, BPF_REG_4, (int16_t) 0, 0x0 }, + { 0xbf, BPF_REG_4, BPF_REG_2, (int16_t) 0, 0x0 }, +- { 0x7, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x9 }, +- { 0x2d, BPF_REG_4, BPF_REG_3, (int16_t) 29, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 1, 0x0 }, ++ { 0x7, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x1 }, ++ { 0x2d, BPF_REG_4, BPF_REG_3, (int16_t) 30, 0x0 }, ++ { 0x7, BPF_REG_2, BPF_REG_0, (int16_t) 0, 0xfffffff9 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 0, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x38 }, +- { 0x71, BPF_REG_3, BPF_REG_2, (int16_t) 2, 0x0 }, ++ { 0x71, BPF_REG_3, BPF_REG_2, (int16_t) 1, 0x0 }, + { 0x67, BPF_REG_3, BPF_REG_0, (int16_t) 0, 0x30 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 3, 0x0 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 2, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x28 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 4, 0x0 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 3, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x20 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 5, 0x0 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 4, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x18 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 6, 0x0 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 5, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x10 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 7, 0x0 }, ++ { 0x71, BPF_REG_4, BPF_REG_2, (int16_t) 6, 0x0 }, + { 0x67, BPF_REG_4, BPF_REG_0, (int16_t) 0, 0x8 }, + { 0x4f, BPF_REG_3, BPF_REG_4, (int16_t) 0, 0x0 }, +- { 0x71, BPF_REG_2, BPF_REG_2, (int16_t) 8, 0x0 }, ++ { 0x71, BPF_REG_2, BPF_REG_2, (int16_t) 7, 0x0 }, + { 0x4f, BPF_REG_3, BPF_REG_2, (int16_t) 0, 0x0 }, + { 0x7b, BPF_REG_10, BPF_REG_3, (int16_t) 65528, 0x0 }, + { 0xbf, BPF_REG_3, BPF_REG_10, (int16_t) 0, 0x0 }, +@@ -86,3 +93,4 @@ ngx_bpf_program_t ngx_quic_reuseport_helper = { + .license = "BSD", + .type = BPF_PROG_TYPE_SK_REUSEPORT, + }; ++ +diff --git a/src/event/quic/ngx_event_quic_connid.c b/src/event/quic/ngx_event_quic_connid.c +index f508682..9046db1 100644 +--- a/src/event/quic/ngx_event_quic_connid.c ++++ b/src/event/quic/ngx_event_quic_connid.c +@@ -15,6 +15,8 @@ + #if (NGX_QUIC_BPF) + static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id); + #endif ++static ngx_int_t ngx_quic_dcid_encode_server_info(ngx_connection_t *c, ++ u_char *id); + static ngx_int_t ngx_quic_retire_client_id(ngx_connection_t *c, + ngx_quic_client_id_t *cid); + static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c, +@@ -38,6 +40,12 @@ ngx_quic_create_server_id(ngx_connection_t *c, u_char *id) + } + #endif + ++ /* encode server info into DCID for L4LB/DPVS */ ++ if (ngx_quic_dcid_encode_server_info(c, id) != NGX_OK) { ++ ngx_log_error(NGX_LOG_ERR, c->log, 0, ++ "quic server info failed to be encoded"); ++ } ++ + return NGX_OK; + } + +@@ -69,6 +77,60 @@ ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id) + + #endif + ++/* ++ * L4LB/DPVS QUIC Connction ID Format { ++ * First Octet (8), ++ * L3 Address Length (3), ++ * L4 Address Flag (1), ++ * L3 Address (8...64), ++ * [ L4 Address (16) ] ++ * Nonce (32...140) ++ * } ++ * ++ * Specifically for this case: ++ * L3 Address Length := 3 (IPv4), 4 (IPv6) ++ * L4 Address Flag := 0 ++ */ ++static ngx_int_t ++ngx_quic_dcid_encode_server_info(ngx_connection_t *c, u_char *id) ++{ ++ unsigned int len; ++ u_char *addr, *ptr; ++ struct sockaddr *sa; ++ ++ sa = c->local_sockaddr; ++ if (sa->sa_family == AF_INET) { ++ addr = (u_char *)(&((struct sockaddr_in *)sa)->sin_addr); ++ len = 3; ++ addr += (4 - len); ++ } else if (sa->sa_family == AF_INET6) { ++ addr = (u_char *)(&((struct sockaddr_in6 *)sa)->sin6_addr); ++ len = 4; ++ addr += (16 - len); ++ } else { ++ return NGX_OK; ++ } ++ ++ if (len + sizeof(uint64_t) + 2 > NGX_QUIC_SERVER_CID_LEN) { ++ return NGX_ERROR; ++ } ++ ++ ptr = id; ++ ptr++; ++ ++ *ptr = 0; ++ *ptr++ = (((len - 1) & 0x7) << 5) | ((*addr >> 4) & 0xf); ++ ++ while (--len > 0) { ++ *ptr++ = ((*addr & 0xf) << 4) | ((*(addr+1) >> 4) & 0xf); ++ addr++; ++ } ++ ++ *ptr &= 0xf; ++ *ptr |= ((*addr & 0xf) << 4); ++ ++ return NGX_OK; ++} + + ngx_int_t + ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c, +diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c +index 19670a6..1cbec6c 100644 +--- a/src/event/quic/ngx_event_quic_transport.c ++++ b/src/event/quic/ngx_event_quic_transport.c +@@ -2198,5 +2198,6 @@ ngx_quic_create_close(u_char *p, ngx_quic_frame_t *f) + void + ngx_quic_dcid_encode_key(u_char *dcid, uint64_t key) + { ++ dcid += (NGX_QUIC_SERVER_CID_LEN - sizeof(key)); + (void) ngx_quic_write_uint64(dcid, key); + } +-- +1.8.3.1 + diff --git a/pic/dpvs.drawio b/pic/dpvs.drawio new file mode 100644 index 000000000..9fa069975 --- /dev/null +++ b/pic/dpvs.drawio @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pic/dpvs.png b/pic/dpvs.png index 25356a44e..ecf6c4217 100644 Binary files a/pic/dpvs.png and b/pic/dpvs.png differ diff --git a/pic/dpvs.svg b/pic/dpvs.svg new file mode 100644 index 000000000..fa065ce3d --- /dev/null +++ b/pic/dpvs.svg @@ -0,0 +1,4 @@ + + + +
FWD
FWD
FWD
FWD
FWD
FWD
FWD
FWD
Lightweight IP Stack
Lightweight IP Stack
1
1
2
2
3
3
4
4
0
0
Control
Plane
Control...
UIO, VFIO, Bifurcated Driver
UIO, VFIO, Bifurcated Driver
User Space
User Space
Kernel Space
Kernel Space
Multi-Cores
Multi-Cores
Lockless
Lockless
  • High Performance: Multi-Cores, Lockless
  • All-in-One: Reverse Proxy(FNAT|DR|Tunnel|NAT64), SNAT
  • Security: SYN-proxy, Allow/Deny ACL
  • IDC Friendly: VLAN, Bonding, IP Tunnel
  • QoS: Connection Limitation,  Traffic Control
High Performance: Multi-Cores, LocklessAll-in-One: Reverse Proxy(FNAT|DR|Tunnel|NAT64), SNATSecurity:...
Users
Users
Internet
Internet
Router
Router
DPVS Cluster
DPVS Cluster
Backend Servers
Backend Servers
\ No newline at end of file diff --git a/pic/modules.png b/pic/modules.png index 6a00fca3e..46be449d4 100644 Binary files a/pic/modules.png and b/pic/modules.png differ diff --git a/pic/modules.svg b/pic/modules.svg new file mode 100644 index 000000000..809ab2220 --- /dev/null +++ b/pic/modules.svg @@ -0,0 +1,4 @@ + + + +
Traffic Control
Traffic Contr...
DPDK PMD Driver
DPDK PMD Driver
netif_addr
netif_addr
netif_flow
netif_flow
netif_port
netif_port
bonding
bonding
vlan
vlan
ip-tunnel
ip-tunnel
kni
kni
Job Scheduler
Job Scheduler
INET
INET
inetaddr
inetaddr
Routing
Routing
ICMPv6
ICMPv6
ICMP
ICMP
IPv4
IPv4
ndisc/ARP
ndisc/ARP
IPv6
IPv6
ipset
ipset
LLDP
LLDP
stats
stats
conn
conn
redirect
redirect
Protocol
TCP|UDP|SCTP
Protocol...
Scheduler
wrr|wlc|conhash|mh|fo
Scheduler...
serv%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%26lt%3Bspan%20style%3D%26quot%3Bfont-size%3A%2016px%3B%26quot%3B%26gt%3Bconn%26lt%3B%2Fspan%26gt%3B%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23dae8fc%3BstrokeColor%3D%236c8ebf%3BgradientColor%3D%237ea6e0%3BfontStyle%3D1%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%2250%22%20y%3D%22400%22%20width%3D%2280%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3Eice
serv%3CmxGrap...
dest
dest
ProxyProto
ProxyProto
xmit
xmit
ipvsadm
ipvsadm
dpip
dpip
keepalived
keepalived
lcore-msg
(lockless)
lcore-msg...
sapool
sapool
timer
timer
kmod
(toa|uoa)
kmod...
config
config
dpvs.conf
dpvs.conf
keepalived
.conf
keepaliv...
healthcheck
healthcheck
dpvs-agent
dpvs-agent
Control Plane
Control Plane
API
API
Network
Devices
Network...
Lite Network Stack
Lite Ne...
Load Balancer
Load Ba...
Tools
Tools
ACL
ACL
\ No newline at end of file diff --git a/scripts/dpdk-build.sh b/scripts/dpdk-build.sh index 8416120b0..600a263d4 100755 --- a/scripts/dpdk-build.sh +++ b/scripts/dpdk-build.sh @@ -5,13 +5,12 @@ build_options="-Denable_kmods=true" debug_options="-Dbuildtype=debug -Dc_args=-DRTE_MALLOC_DEBUG" -dpdkver=20.11.1 # default dpdk version (use stable version) +dpdkver=20.11.10 # default dpdk version (use stable version) tarball=dpdk-${dpdkver}.tar.xz srcdir=dpdk-stable-$dpdkver -workdir=$(pwd)/dpdk # default work directory -patchdir=$(pwd)/patch/dpdk-stable-$dpdkver # default dpdk patch directory - +workdir="" +patchdir="" function help() { @@ -23,35 +22,29 @@ function help() echo -e "\033[31m -p specify the dpdk patch directory, default $(pwd)/patch/dpdk-stable-$dpdkver\033[0m" } -function getfullpath() +function set_dpdk_version() { - local dir=$(dirname $1) - local base=$(basename $1) - if test -d ${dir}; then - pushd ${dir} >/dev/null 2>&1 - echo ${PWD}/${base} - popd >/dev/null 2>&1 - return 0 - fi - return 1 + dpdkver=$1 + tarball=dpdk-${dpdkver}.tar.xz + srcdir=dpdk-stable-$dpdkver } function set_work_directory() { [ ! -d $1 ] && return 1 - workdir=$(getfullpath $1)/dpdk + workdir=$(realpath $1)/dpdk } function set_patch_directory() { [ ! -d $1 ] && return 1 - patchdir=$(getfullpath $1) + patchdir=$(realpath $1) } ## parse args while getopts "hw:p:dv:" OPT; do case $OPT in - v) dpdkver=$OPTARG;; + v) set_dpdk_version $OPTARG;; w) set_work_directory $OPTARG ;; p) set_patch_directory $OPTARG;; d) build_options="${build_options} ${debug_options}";; @@ -59,6 +52,9 @@ while getopts "hw:p:dv:" OPT; do esac done +[ -z "$workdir" ] && workdir=$(pwd)/dpdk # use default work directory +[ -z "$patchdir" ] && patchdir=$(pwd)/patch/dpdk-stable-$dpdkver # use default dpdk patch directory + [ ! -d $workdir ] && mkdir $workdir echo -e "\033[32mwork directory: $workdir\033[0m" diff --git a/src/VERSION b/src/VERSION index 7a8b5d13b..f97d35bb7 100755 --- a/src/VERSION +++ b/src/VERSION @@ -1,44 +1,37 @@ #!/bin/sh # program: dpvs -# Dec 19, 2023 # +# Sep 19, 2024 # ## # Features -# - New tool: **dpvs-agent**, a management daemon tool for dpvs based on OpenAPI. -# - New tool: **healthcheck**, a service health check daemon tool cooperating with dpvs-agent. -# - Dpvs: Develop **passive health check** methods for tcp and bidirectional udp backends. -# - Dpvs: Add supports for **Proxy Protocol** with both v1 and v2 versions. -# - Dpvs: Add supports for extended statistics of ethernet devices. -# - Dpvs: Add configuration file and dpip supports for allmulticast setting switch. -# - Build: Transfer all build configurations to a top-level file `config.mk`. -# - Containerization: Draft a Dockerfile and a tutorial document to build and run dpvs in container. +# - dpvs: Support QUIC/HTTP3, add nginx patches and facilitating code snippets for use of quic. +# - dpvs: Support SCTP forwarding implementation. +# - dpvs: Support LLDP protocol. +# - dpvs: Update default dpdk version to dpdk-stable-20.11.10. +# - dpvs: IPVS supports ipset based allow/deny list which allows for cidr acl rule. +# - dpvs: Support IPv6 link-local address auto configuration. +# - tools: Add ipset supports in dpvs-agent. +# - tools: Add snapshot caches for dpvs-agent virtual server apis. +# - doc: Update README.md. # # Bugfixes -# - Dpvs: Protect toa from source address spoofing attack and increase success ratio for source address delievery via toa. -# - Dpvs: Adjust tcp window scale in outbound direction for synproxy to improve throughput in bulk upload cases. -# - Dpvs: Fix timer inaccuracy problem when timing over 524s. -# - Dpvs: Fix the crash problem caused by ether address list buffer overflow. -# - Dpvs: Fix the crash problem caused by dividing by zero when bonding slaves attempt to send packets out. -# - Dpvs: Fix the crash problem caused by inconsistent data structures of `dp_vs_dest_compat` between dpvs and keepalived. -# - Dpvs: Correct ipo option length for judgement of branching to standalone uoa. -# - Dpvs: Inhibit setting multicast ether address from slave lcores. -# - Dpvs: Fix service flag conflicts of synproxy and expire-quiescent. -# - Dpvs: Fix the chaos use of flag, flags and fwdmode in dest and service structures. -# - Dpvs: Fix service flush function not usable problem. -# - Dpvs: Fix invalid port problem when getting verbose information of netif devices. -# - Dpvs: Use atomic operation to generate packet id for ipv4 header. -# - Dpvs: Remove fragile implementations of strategy routing for snat. -# - Dpvs: Remove the stale config item "ipc_msg/unix_domain". -# - Keepalived: Do not delete and re-add vs/rs to eliminate service disturbances at reload. -# - Keepalived: Fix a carsh problem caused by missing definition of allowlist/denylist config items. -# - Ipvsadm: Add `conn-timeout` configuration option for service. -# - Ipvsadm: Fix the ambiguous use of '-Y' configuration option. -# - Ipvsadm: Fix icmpv6 configuration option `-1` lost problem.. -# - Ipvsadm: Update help text, including supported schedulers, laddr and allow/deny ip list. -# - Dpip: Fix line break problem in help message. -# - Uoa: Enable ipv6 with a macro for uoa example server. +# - dpvs: Fix multicast address sync problems and add dpip supports for multicast address lookup. +# - dpvs: Fix build errors and warnings with gcc verison 8.0+. +# - dpvs: Fix coredump problem when starting dpvs with insufficient memory. +# - dpvs: Use dpdk random generator in critical datapath for performance enhancement. +# - dpvs: Fix ipset default address family problem. +# - dpvs: Fix segmentation fault problem when running on machines whose cpu number is over DPVS_MAX_LCORE. +# - dpvs: Refactor netif_rte_port_alloc with netif_alloc. +# - dpvs: Fix prolems in IPv6 all-nodes and all-routers address initialization. +# - dpvs: Fix memory corruption problem when retrieving nic's xstats. +# - tools: Fix concurrency racing problem when dpvs-agent and healthcheck changing rs simultaneously. +# - tools: Fix healthchech bad icmp checksum problem ocasionally appeared in udp and udpping checkers. +# - tools: Fix keepalived quorum up script not excuted problem when old rs removed and new ones added in a reload. +# - tools: Fix ipvsadm local IP won't remove problem. +# - tools: Fix ipset list-all problem and improve efficiency. +# - tools: Fix dpip delay problem when list empty ipset with sorting enabled. # export VERSION=1.9 -export RELEASE=6 +export RELEASE=8 echo $VERSION-$RELEASE diff --git a/src/common.c b/src/common.c index c10505cf4..8452ef806 100644 --- a/src/common.c +++ b/src/common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +247,26 @@ int linux_hw_mc_del(const char *ifname, const uint8_t hwma[ETH_ALEN]) return linux_hw_mc_mod(ifname, hwma, false); } +int linux_ifname2index(const char *ifname) +{ + int sockfd; + struct ifreq ifr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + return -1; + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); + if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) { + close(sockfd); + return -1; + } + close(sockfd); + + return ifr.ifr_ifindex; +} + ssize_t readn(int fd, void *vptr, size_t n) { size_t nleft; @@ -320,3 +341,198 @@ ssize_t sendn(int fd, const void *vptr, size_t n, int flags) return (n); } +static uint8_t hex_char2num(char hex) +{ + if (hex >= '0' && hex <= '9') + return hex - '0'; + if (hex >= 'A' && hex <= 'F') + return hex - 'A' + 10; + if (hex >= 'a' && hex <= 'f') + return hex - 'a' + 10; + return 255; +} + +int hexstr2binary(const char *hexstr, size_t len, uint8_t *buf, size_t buflen) +{ + int i, j; + + for (i = 0, j = 0; i + 1 < len && j < buflen; i += 2, j++) + buf[j] = (hex_char2num(hexstr[i]) << 4) | hex_char2num(hexstr[i+1]); + + return j; +} + +#define num2hexchar(b) (((b) > 9) ? ((b) - 0xa + 'A') : ((b) + '0')) +int binary2hexstr(const uint8_t *hex, size_t len, char *buf, size_t buflen) +{ + size_t i, j; + + for (i = 0, j = 0; i < len && j + 1 < buflen; i++, j += 2) { + buf[j] = num2hexchar((hex[i] & 0xf0) >> 4); + buf[j+1] = num2hexchar(hex[i] & 0x0f); + } + + return j; +} + +int binary2print(const uint8_t *hex, size_t len, char *buf, size_t buflen) +{ + size_t i, j; + + for (i = 0, j = 0; i < len && j < buflen; i++) { + if (isprint(hex[i])) { + buf[j++] = hex[i]; + if (j >= buflen) + break; + } else { + if (j + 2 >= buflen) + break; + buf[j] = '\\'; + buf[j+1] = num2hexchar((hex[i] & 0xf0) >> 4); + buf[j+2] = num2hexchar(hex[i] & 0x0f); + j += 2; + } + } + + return j; +} + +static int is_link_local(struct sockaddr *addr) +{ + unsigned char *addrbytes; + if (addr->sa_family == AF_INET6) { + addrbytes = (unsigned char *)(&((struct sockaddr_in6 *)addr)->sin6_addr); + return (addrbytes[0] == 0xFE) && ((addrbytes[1] & 0xC0) == 0x80); /* fe80::/10 */ + } + return 0; +} + +int mask2prefix(const struct sockaddr *addr) +{ + int i, j; + int pfxlen, addrlen; + unsigned char *mask; + + if (!addr) + return -1; + + if (addr->sa_family == AF_INET) { + mask = (unsigned char *)&((struct sockaddr_in *)addr)->sin_addr; + addrlen = 4; + } else if (addr->sa_family == AF_INET6) { + mask = (unsigned char *)&((struct sockaddr_in6 *)addr)->sin6_addr; + addrlen = 16; + } else { + return -1; + } + + pfxlen = 0; + for (i = 0; i < addrlen; i++) { + for (j = 7; j >= 0; j--) { + if (mask[i] & (1U << j)) + ++pfxlen; + else + return pfxlen; + } + } + return pfxlen; +} + +int get_host_addr(const char *ifname, struct sockaddr_storage *result4, + struct sockaddr_storage *result6, char *ifname4, char *ifname6) +{ + struct ifaddrs *ifa_head, *ifa; + int found_v4 = 0, found_v6 = 0; + int pfxlen, pfxlen_v4 = 0, pfxlen_v6 = 0; + + if (getifaddrs(&ifa_head) == -1) + return -1; + + /* addresses on ifname take precedence */ + if (ifname) { + for (ifa = ifa_head; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_flags & IFF_LOOPBACK || + !(ifa->ifa_flags & IFF_UP) || + !(ifa->ifa_flags & IFF_RUNNING)) + continue; + if (is_link_local(ifa->ifa_addr)) + continue; + if (strcmp(ifname, ifa->ifa_name) == 0) { + pfxlen = mask2prefix(ifa->ifa_netmask); + if (ifa->ifa_addr->sa_family == AF_INET) { + if (!pfxlen_v4 || (pfxlen > 0 && pfxlen < pfxlen_v4)) { + if (result4) + memcpy(result4, ifa->ifa_addr, sizeof(struct sockaddr_in)); + if (ifname4) { + strncpy(ifname4, ifa->ifa_name, IFNAMSIZ-1); + ifname4[IFNAMSIZ-1] = '\0'; + } + found_v4 = 1; + pfxlen_v4 = pfxlen > 0 ? pfxlen : 32; + } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + if (!pfxlen_v6 || (pfxlen > 0 && pfxlen < pfxlen_v6)) { + if (result6) + memcpy(result6, ifa->ifa_addr, sizeof(struct sockaddr_in6)); + if (ifname6) { + strncpy(ifname6, ifa->ifa_name, IFNAMSIZ-1); + ifname6[IFNAMSIZ-1] = '\0'; + } + found_v6 = 1; + pfxlen_v6 = pfxlen > 0 ? pfxlen : 128; + } + } + } + } + } + + /* try to find address on other interfaces */ + if (!found_v4 || !found_v6) { + for (ifa = ifa_head; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_flags & IFF_LOOPBACK || + !(ifa->ifa_flags & IFF_UP) || + !(ifa->ifa_flags & IFF_RUNNING)) + continue; + if (is_link_local(ifa->ifa_addr)) + continue; + pfxlen = mask2prefix(ifa->ifa_netmask); + if (ifa->ifa_addr->sa_family == AF_INET) { + if (!pfxlen_v4 || (pfxlen > 0 && pfxlen < pfxlen_v4)) { + if (result4) + memcpy(result4, ifa->ifa_addr, sizeof(struct sockaddr_in)); + if (ifname4) { + strncpy(ifname4, ifa->ifa_name, IFNAMSIZ-1); + ifname4[IFNAMSIZ-1] = '\0'; + } + found_v4 = 1; + pfxlen_v4 = pfxlen > 0 ? pfxlen : 32; + } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + if (!pfxlen_v6 || (pfxlen > 0 && pfxlen < pfxlen_v6)) { + if (result6) + memcpy(result6, ifa->ifa_addr, sizeof(struct sockaddr_in6)); + if (ifname6) { + strncpy(ifname6, ifa->ifa_name, IFNAMSIZ-1); + ifname6[IFNAMSIZ-1] = '\0'; + } + found_v6 = 1; + pfxlen_v6 = pfxlen > 0 ? pfxlen : 128; + } + } + } + } + + freeifaddrs(ifa_head); + + if (found_v4 && found_v6) + return 3; + if (found_v4) + return 1; + if (found_v6) + return 2; + return 0; +} diff --git a/src/ctrl.c b/src/ctrl.c index 303f43b2f..2b23377a9 100644 --- a/src/ctrl.c +++ b/src/ctrl.c @@ -496,7 +496,7 @@ int msg_send(struct dpvs_msg *msg, lcoreid_t cid, uint32_t flags, struct dpvs_ms RTE_LOG(WARNING, MSGMGR, "%s:msg@%p, msg ring of lcore %d quota exceeded\n", __func__, msg, cid); } else if (unlikely(-ENOBUFS == res)) { - RTE_LOG(ERR, MSGMGR, "%s:msg@%p, msg ring of lcore %d is full\n", __func__, msg, res); + RTE_LOG(ERR, MSGMGR, "%s:msg@%p, msg ring of lcore %d is full\n", __func__, msg, cid); add_msg_flags(msg, DPVS_MSG_F_STATE_DROP); rte_atomic16_dec(&msg->refcnt); /* not enqueued, free manually */ return EDPVS_DPDKAPIFAIL; diff --git a/src/dpdk.mk b/src/dpdk.mk index e3c4c2759..81ef04305 100644 --- a/src/dpdk.mk +++ b/src/dpdk.mk @@ -26,24 +26,21 @@ If dpdk has installed already, please ensure the libdpdk.pc file could be found You may fix the problem by setting LIBDPDKPC_PATH (in file src/dpdk.mk) to the path of libdpdk.pc file explicitly endef -# It's noted that pkg-config version 0.29.2 is recommended, +# It's noted that pkg-config version 0.29.2+ is recommended, # pkg-config 0.27.1 would mess up the ld flags when linking dpvs. -PKGCONFIG_VERSION=$(shell pkg-config pkg-config --version) -ifneq "v$(PKGCONFIG_VERSION)" "v0.29.2" -$(warning "The pkg-config version is $(PKGCONFIG_VERSION) but 0.29.2 is recommended.") +PKGCONFIG_VERSION=$(shell pkg-config --version) ifeq "v$(PKGCONFIG_VERSION)" "v0.27.1" -$(error "pkg-config version $(PKGCONFIG_VERSION) isn't supported by dpvs, please use 0.29.2 instead.") -endif +$(error "pkg-config version $(PKGCONFIG_VERSION) isn't supported, require 0.29.2+") endif -ifeq ($(shell pkg-config --exists libdpdk && echo 0),0) -CFLAGS += -DALLOW_EXPERIMENTAL_API $(shell pkg-config --cflags libdpdk) -LIBS += $(shell pkg-config --static --libs libdpdk) -else ifneq ($(wildcard $(LIBDPDKPC_PATH)),) CFLAGS += -DALLOW_EXPERIMENTAL_API $(shell PKG_CONFIG_PATH=$(LIBDPDKPC_PATH) pkg-config --cflags libdpdk) LIBS += $(shell PKG_CONFIG_PATH=$(LIBDPDKPC_PATH) pkg-config --static --libs libdpdk) else +ifeq ($(shell pkg-config --exists libdpdk && echo 0),0) +CFLAGS += -DALLOW_EXPERIMENTAL_API $(shell pkg-config --cflags libdpdk) +LIBS += $(shell pkg-config --static --libs libdpdk) +else $(error $(PKG_CONFIG_ERR_MSG)) endif endif diff --git a/src/global_conf.c b/src/global_conf.c index 9935d8f79..4a28edc79 100644 --- a/src/global_conf.c +++ b/src/global_conf.c @@ -20,6 +20,7 @@ #include "global_conf.h" #include "global_data.h" #include "log.h" +#include "lldp.h" bool g_dpvs_pdump = false; @@ -103,6 +104,13 @@ static int set_log_file(const char *log_file) return EDPVS_OK; } +static void global_defs_handler(vector_t tokens) +{ + // initilize config to default value + g_dpvs_log_tslen = 0; + dpvs_lldp_disable(); +} + static void log_level_handler(vector_t tokens) { char *log_level = set_value(tokens); @@ -189,6 +197,22 @@ static void kni_handler(vector_t tokens) FREE_PTR(str); } +static void lldp_handler(vector_t tokens) +{ + char *str = set_value(tokens); + assert(str); + if (strcasecmp(str, "on") == 0) + dpvs_lldp_enable(); + else if (strcasecmp(str, "off") == 0) + dpvs_lldp_disable(); + else + RTE_LOG(WARNING, CFG_FILE, "invalid lldp config: %s\n", str); + + RTE_LOG(INFO, CFG_FILE, "lldp = %s\n", dpvs_lldp_is_enabled() ? "on" : "off"); + + FREE_PTR(str); +} + #ifdef CONFIG_DPVS_PDUMP static void pdump_handler(vector_t tokens) { @@ -209,13 +233,14 @@ static void pdump_handler(vector_t tokens) void install_global_keywords(void) { - install_keyword_root("global_defs", NULL); + install_keyword_root("global_defs", global_defs_handler); install_keyword("log_level", log_level_handler, KW_TYPE_NORMAL); install_keyword("log_file", log_file_handler, KW_TYPE_NORMAL); install_keyword("log_async_mode", log_async_mode_handler, KW_TYPE_INIT); install_keyword("log_with_timestamp", log_with_timestamp_handler, KW_TYPE_NORMAL); install_keyword("log_async_pool_size", log_async_pool_size_handler, KW_TYPE_INIT); install_keyword("kni", kni_handler, KW_TYPE_INIT); + install_keyword("lldp", lldp_handler, KW_TYPE_NORMAL); #ifdef CONFIG_DPVS_PDUMP install_keyword("pdump", pdump_handler, KW_TYPE_INIT); #endif diff --git a/src/iftraf.c b/src/iftraf.c index a03277402..5d36e6657 100644 --- a/src/iftraf.c +++ b/src/iftraf.c @@ -678,7 +678,8 @@ static int iftraf_pkt_deliver(int af, struct rte_mbuf *mbuf, struct netif_port * struct rte_ipv4_hdr *ip4h = ip4_hdr(mbuf); if (unlikely(ip4h->next_proto_id != IPPROTO_TCP && - ip4h->next_proto_id != IPPROTO_UDP)) { + ip4h->next_proto_id != IPPROTO_UDP && + ip4h->next_proto_id != IPPROTO_SCTP)) { RTE_LOG(DEBUG, IFTRAF, "%s: unspported proto[core: %d, proto: %d]\n", __func__, cid, ip4h->next_proto_id); @@ -740,7 +741,8 @@ static int iftraf_pkt_deliver(int af, struct rte_mbuf *mbuf, struct netif_port * uint8_t ip6nxt = ip6h->ip6_nxt; if (unlikely(ip6nxt != IPPROTO_TCP && - ip6nxt != IPPROTO_UDP)) { + ip6nxt != IPPROTO_UDP && + ip6nxt != IPPROTO_SCTP)) { RTE_LOG(DEBUG, IFTRAF, "%s: unspported proto[core: %d, proto: %d]\n", __func__, cid, ip6nxt); diff --git a/src/inet.c b/src/inet.c index d5ebcdea1..430babf3c 100644 --- a/src/inet.c +++ b/src/inet.c @@ -28,6 +28,7 @@ #include "icmp.h" #include "icmp6.h" #include "inetaddr.h" +#include "lldp.h" #define INET #define RTE_LOGTYPE_INET RTE_LOGTYPE_USER1 @@ -99,6 +100,8 @@ int inet_init(void) return err; if ((err = inet_addr_init()) != 0) return err; + if ((err = dpvs_lldp_init()) != 0) + return err; return EDPVS_OK; } @@ -107,6 +110,8 @@ int inet_term(void) { int err; + if ((err = dpvs_lldp_term()) != 0) + return err; if ((err = inet_addr_term()) != 0) return err; if ((err = icmpv6_term()) != 0) diff --git a/src/inetaddr.c b/src/inetaddr.c index d878ce06f..87c1985f0 100644 --- a/src/inetaddr.c +++ b/src/inetaddr.c @@ -16,6 +16,7 @@ * */ #include +#include #include "dpdk.h" #include "ctrl.h" #include "netif.h" @@ -24,6 +25,7 @@ #include "sa_pool.h" #include "ndisc.h" #include "route.h" +#include "ipv6.h" #include "route6.h" #include "inetaddr.h" #include "conf/inetaddr.h" @@ -75,14 +77,14 @@ static uint32_t ifa_msg_seq(void) return counter++; } -static inline struct inet_device *dev_get_idev(const struct netif_port *dev) +struct inet_device *dev_get_idev(const struct netif_port *dev) { assert(dev && dev->in_ptr); rte_atomic32_inc(&dev->in_ptr->refcnt); return dev->in_ptr; } -static inline void idev_put(struct inet_device *idev) +void idev_put(struct inet_device *idev) { rte_atomic32_dec(&idev->refcnt); } @@ -90,15 +92,17 @@ static inline void idev_put(struct inet_device *idev) static inline void imc_hash(struct inet_ifmcaddr *imc, struct inet_device *idev) { list_add(&imc->d_list, &idev->this_ifm_list); - rte_atomic32_inc(&imc->refcnt); + ++imc->refcnt; + ++idev->this_ifm_cnt; } static inline void imc_unhash(struct inet_ifmcaddr *imc) { - assert(rte_atomic32_read(&imc->refcnt) > 1); + assert(imc->refcnt> 1); list_del(&imc->d_list); - rte_atomic32_dec(&imc->refcnt); + --imc->refcnt; + --imc->idev->this_ifm_cnt; } static struct inet_ifmcaddr *imc_lookup(int af, const struct inet_device *idev, @@ -109,7 +113,7 @@ static struct inet_ifmcaddr *imc_lookup(int af, const struct inet_device *idev, list_for_each_entry(imc, &idev->ifm_list[cid], d_list) { if (inet_addr_equal(af, &imc->addr, maddr)) { - rte_atomic32_inc(&imc->refcnt); + ++imc->refcnt; return imc; } } @@ -121,7 +125,7 @@ static void imc_put(struct inet_ifmcaddr *imc) { char ipstr[64]; - if (rte_atomic32_dec_and_test(&imc->refcnt)) { + if (--imc->refcnt == 0) { RTE_LOG(DEBUG, IFA, "[%02d] %s: del mcaddr %s\n", rte_lcore_id(), __func__, inet_ntop(imc->af, &imc->addr, ipstr, sizeof(ipstr))); @@ -136,20 +140,27 @@ static int idev_mc_add(int af, struct inet_device *idev, struct inet_ifmcaddr *imc; char ipstr[64]; - imc = imc_lookup(af, idev, maddr); - if (imc) { - imc_put(imc); - return EDPVS_EXIST; + if (imc_lookup(af, idev, maddr)) { + /* + * Hold the imc and return. + * + * Multiple IPv6 unicast address may be mapped to one IPv6 solicated-node + * multicast address. So increase the imc refcnt each time idev_mc_add called. + * + * Possibly imc added repeated? No, at least for now. The imc is set within the + * rigid program, not allowing user to configure it. + * */ + return EDPVS_OK; } imc = rte_calloc(NULL, 1, sizeof(struct inet_ifmcaddr), RTE_CACHE_LINE_SIZE); if (!imc) return EDPVS_NOMEM; - imc->af = af; - imc->idev = idev; - imc->addr = *maddr; - rte_atomic32_init(&imc->refcnt); + imc->af = af; + imc->idev = idev; + imc->addr = *maddr; + imc->refcnt = 1; imc_hash(imc, idev); @@ -169,7 +180,10 @@ static int idev_mc_del(int af, struct inet_device *idev, if (!imc) return EDPVS_NOTEXIST; - imc_unhash(imc); + if (--imc->refcnt == 2) { + imc_unhash(imc); + } + imc_put(imc); return EDPVS_OK; @@ -192,8 +206,6 @@ static int ifa_add_del_mcast(struct inet_ifaddr *ifa, bool add, bool is_master) if (add) { err = idev_mc_add(ifa->af, ifa->idev, &iaddr); - if (EDPVS_EXIST == err) - return EDPVS_OK; if (err) return err; if (is_master) { @@ -222,18 +234,13 @@ static int ifa_add_del_mcast(struct inet_ifaddr *ifa, bool add, bool is_master) } /* add ipv6 multicast address after port start */ -int idev_add_mcast_init(void *args) +static int __idev_inet6_mcast_init(struct inet_device *idev) { int err; - struct inet_device *idev; union inet_addr all_nodes, all_routers; struct rte_ether_addr eaddr_nodes, eaddr_routers; bool is_master = (rte_lcore_id() == g_master_lcore_id); - struct netif_port *dev = (struct netif_port *) args; - - idev = dev_get_idev(dev); - memset(&eaddr_nodes, 0, sizeof(eaddr_nodes)); memset(&eaddr_routers, 0, sizeof(eaddr_routers)); @@ -263,7 +270,6 @@ int idev_add_mcast_init(void *args) goto free_idev_routers; } - idev_put(idev); return EDPVS_OK; free_idev_routers: @@ -274,10 +280,176 @@ int idev_add_mcast_init(void *args) free_idev_nodes: idev_mc_del(AF_INET6, idev, &all_nodes); errout: - idev_put(idev); return err; } +static int inet6_addr_gen_eui64(struct inet_device *idev, struct in6_addr *addr) +{ + unsigned char hwaddr[6]; + unsigned char *eui = &addr->s6_addr[8]; + + rte_memcpy(hwaddr, &idev->dev->addr, 6); + eui[0] = hwaddr[0] ^ 0x02; + eui[1] = hwaddr[1]; + eui[2] = hwaddr[2]; + eui[3] = 0xFF; + eui[4] = 0xFE; + eui[5] = hwaddr[3]; + eui[6] = hwaddr[4]; + eui[7] = hwaddr[5]; + + return EDPVS_OK; +} + +static int inet6_addr_gen_stable(struct in6_addr secret, struct inet_device *idev, struct in6_addr *addr) +{ +#define MAX_RETRY 8 + struct in6_addr temp; + union { + unsigned char data[SHA256_DIGEST_LENGTH]; + uint32_t data_word[2]; + } md; + struct { + struct in6_addr secret; + uint32_t prefix[2]; + struct rte_ether_addr hwaddr; + uint8_t dad_count; + } data; + uint8_t dad_count = 0; + + memset(&data, 0, sizeof(data)); + data.secret = secret; + data.prefix[0] = addr->s6_addr32[0]; + data.prefix[1] = addr->s6_addr32[1]; + data.hwaddr = idev->dev->addr; + while (1) { + data.dad_count = dad_count++; + memset(&md, 0, sizeof(md)); + SHA512((const unsigned char*)&data, sizeof(data), md.data); + temp = *addr; + temp.s6_addr32[2] = md.data_word[0]; + temp.s6_addr32[3] = md.data_word[1]; + if (!ipv6_reserved_interfaceid(&temp)) + break; + if (dad_count >= MAX_RETRY) + return EDPVS_RESOURCE; + } + + *addr = temp; + return EDPVS_OK; +} + +static int inet6_link_local_addr_gen(struct inet_device *idev, struct in6_addr *addr) +{ + const struct ipv6_config *ip6cfg = ip6_config_get(); + + ipv6_addr_set(addr, htonl(0xFE800000), 0, 0, 0); + switch (ip6cfg->addr_gen_mode) { + case IP6_ADDR_GEN_MODE_EUI64: + return inet6_addr_gen_eui64(idev, addr); + case IP6_ADDR_GEN_MODE_NONE: + return EDPVS_DISABLED; + case IP6_ADDR_GEN_MODE_STABLE_PRIVACY: + if (ip6cfg->secret_stable.initialized) + return inet6_addr_gen_stable(ip6cfg->secret_stable.secret, idev, addr); + // fallthrough + case IP6_ADDR_GEN_MODE_RANDOM: + return inet6_addr_gen_stable(ip6cfg->secret_random.secret, idev, addr); + default: + return EDPVS_NOTSUPP; + } + + return EDPVS_OK; +} + +static int ifa_entry_add(const struct ifaddr_action *param); +static int __inet6_link_local_addr_config(struct inet_device *idev, const struct in6_addr *addr) +{ + struct ifaddr_action param; + + memset(¶m, 0, sizeof(param)); + fill_ifaddr_action(AF_INET6, idev->dev, (union inet_addr *)addr, 64, NULL, + 0, 0, IFA_SCOPE_LINK, IFA_F_LINKLOCAL, INET_ADDR_ADD, ¶m); + return ifa_entry_add(¶m); +} + +struct idev_addr_init_args { + struct inet_device *idev; + struct in6_addr link_local_addr; +}; + +static int __idev_addr_init(void *args) +{ + int err; + struct idev_addr_init_args *param = args; + + assert(param && param->idev && param->idev->dev); + + if (rte_lcore_id() >= DPVS_MAX_LCORE) + return EDPVS_OK; + + err = __inet6_link_local_addr_config(param->idev, ¶m->link_local_addr); + if (err != EDPVS_OK) + return err; + + return __idev_inet6_mcast_init(param->idev); +} + +int idev_addr_init(struct inet_device *idev) +{ + int err; + lcoreid_t cid, tcid; + struct dpvs_msg *msg; + struct idev_addr_init_args args; + + // only ipv6 needs address initialization now + if (ip6_config_get()->disable || (idev->flags & IDEV_F_NO_IPV6)) + return EDPVS_OK; + + if (idev->flags & IDEV_F_NO_ROUTE) + return EDPVS_OK; + + cid = rte_lcore_id(); + if (cid != rte_get_main_lcore()) + return EDPVS_NOTSUPP; + + args.idev = idev; + err = inet6_link_local_addr_gen(idev, &args.link_local_addr); + if (err != EDPVS_OK) { + if (EDPVS_DISABLED == err) + return EDPVS_OK; + return err; + } + + // do it on master lcore + err = __idev_addr_init(&args); + if (err != EDPVS_OK) + return err; + + // do it on slave lcores + if (dpvs_state_get() == DPVS_STATE_NORMAL) { + msg = msg_make(MSG_TYPE_IFA_IDEVINIT, ifa_msg_seq(), DPVS_MSG_MULTICAST, + cid, sizeof(args), &args); + if (unlikely(!msg)) + return EDPVS_NOMEM; + err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL); + if (err != EDPVS_OK) { + msg_destroy(&msg); + return err; + } + msg_destroy(&msg); + } else { + rte_eal_mp_remote_launch(__idev_addr_init, &args, SKIP_MAIN); + RTE_LCORE_FOREACH_WORKER(tcid) { + err = rte_eal_wait_lcore(tcid); + if (unlikely(err < 0)) + return err; + } + } + + return EDPVS_OK; +} + /* refer to linux:ipv6_chk_mcast_addr */ bool inet_chk_mcast_addr(int af, struct netif_port *dev, const union inet_addr *group, @@ -1319,6 +1491,17 @@ static int ifa_msg_sync_cb(struct dpvs_msg *msg) return EDPVS_OK; } +static int ifa_msg_idevinit_cb(struct dpvs_msg *msg) +{ + struct idev_addr_init_args *param; + + if (unlikely(!msg || msg->len != sizeof(*param))) + return EDPVS_INVAL; + param = (struct idev_addr_init_args *)(msg->data); + + return __idev_addr_init(param); +} + static int __inet_addr_add(const struct ifaddr_action *param) { int err; @@ -1810,6 +1993,39 @@ static int ifaddr_get_verbose(struct inet_device *idev, struct inet_addr_data_ar return err; } +static int ifmaddr_fill_entries(struct inet_device *idev, struct inet_maddr_array **parray, int *plen) +{ + lcoreid_t cid; + int ifm_cnt, len, off; + struct inet_ifmcaddr *ifm; + struct inet_maddr_array *array; + + cid = rte_lcore_id(); + ifm_cnt = idev->ifm_cnt[cid]; + + len = sizeof(struct inet_maddr_array) + ifm_cnt * sizeof(struct inet_maddr_entry); + array = rte_calloc(NULL, 1, len, RTE_CACHE_LINE_SIZE); + if (unlikely(!array)) + return EDPVS_NOMEM; + + off = 0; + list_for_each_entry(ifm, &idev->ifm_list[cid], d_list) { + strncpy(array->maddrs[off].ifname, ifm->idev->dev->name, + sizeof(array->maddrs[off].ifname) - 1); + array->maddrs[off].maddr = ifm->addr; + array->maddrs[off].af = ifm->af; + array->maddrs[off].flags = ifm->flags; + array->maddrs[off].refcnt = ifm->refcnt; + if (++off >= ifm_cnt) + break; + } + array->nmaddr = off; + + *parray = array; + *plen = len; + return EDPVS_OK; +} + static int ifa_sockopt_set(sockoptid_t opt, const void *conf, size_t size) { struct netif_port *dev; @@ -1894,7 +2110,7 @@ static int ifa_sockopt_agent_get(sockoptid_t opt, const void *conf, size_t size, struct inet_device *idev = NULL; struct inet_addr_front *array = NULL; const struct inet_addr_entry *entry = conf; - int len; + int len = 0; int err; if (entry->af != AF_INET && entry->af != AF_INET6 && entry->af != AF_UNSPEC) { @@ -1944,60 +2160,92 @@ static int ifa_sockopt_get(sockoptid_t opt, const void *conf, size_t size, int err, len = 0; struct netif_port *dev; struct inet_device *idev = NULL; + struct inet_addr_data_array *array = NULL; const struct inet_addr_param *param = conf; - if (!conf || size < sizeof(struct inet_addr_param) || !out || !outsize) + struct inet_maddr_array *marray = NULL; + const char *ifname = conf; + + if (!conf || !out || !outsize) return EDPVS_INVAL; - if (opt != SOCKOPT_GET_IFADDR_SHOW) - return EDPVS_NOTSUPP; + switch (opt) { + case SOCKOPT_GET_IFADDR_SHOW: + if (size < sizeof(struct inet_addr_param) || param->ifa_ops != INET_ADDR_GET) + return EDPVS_INVAL; - if (param->ifa_ops != INET_ADDR_GET) - return EDPVS_INVAL; + if (param->ifa_entry.af != AF_INET && + param->ifa_entry.af != AF_INET6 && + param->ifa_entry.af != AF_UNSPEC) + return EDPVS_NOTSUPP; - if (param->ifa_entry.af != AF_INET && - param->ifa_entry.af != AF_INET6 && - param->ifa_entry.af != AF_UNSPEC) - return EDPVS_NOTSUPP; + if (strlen(param->ifa_entry.ifname)) { + dev = netif_port_get_by_name(param->ifa_entry.ifname); + if (!dev) { + RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", + __func__, param->ifa_entry.ifname); + return EDPVS_NOTEXIST; + } + idev = dev_get_idev(dev); + if (!idev) + return EDPVS_RESOURCE; + } + + if (param->ifa_ops_flags & IFA_F_OPS_VERBOSE) + err = ifaddr_get_verbose(idev, &array, &len); + else if (param->ifa_ops_flags & IFA_F_OPS_STATS) + err = ifaddr_get_stats(idev, &array, &len); + else + err = ifaddr_get_basic(idev, &array, &len); + + if (err != EDPVS_OK) { + RTE_LOG(WARNING, IFA, "%s: fail to get inet addresses -- %s!\n", + __func__, dpvs_strerror(err)); + return err; + } + + if (idev) + idev_put(idev); - if (strlen(param->ifa_entry.ifname)) { - dev = netif_port_get_by_name(param->ifa_entry.ifname); + if (array) { + array->ops = INET_ADDR_GET; + array->ops_flags = param->ifa_ops_flags; + } + + *out = array; + *outsize = len; + break; + case SOCKOPT_GET_IFMADDR_SHOW: + if (!size || strlen(ifname) == 0) + return EDPVS_INVAL; + + dev = netif_port_get_by_name(ifname); if (!dev) { - RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", - __func__, param->ifa_entry.ifname); + RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", __func__, ifname); return EDPVS_NOTEXIST; } - idev = dev_get_idev(dev); if (!idev) return EDPVS_RESOURCE; - } - - if (param->ifa_ops_flags & IFA_F_OPS_VERBOSE) - err = ifaddr_get_verbose(idev, &array, &len); - else if (param->ifa_ops_flags & IFA_F_OPS_STATS) - err = ifaddr_get_stats(idev, &array, &len); - else - err = ifaddr_get_basic(idev, &array, &len); - if (err != EDPVS_OK) { - RTE_LOG(WARNING, IFA, "%s: fail to get inet addresses -- %s!\n", - __func__, dpvs_strerror(err)); - return err; - } + err = ifmaddr_fill_entries(idev, &marray, &len); + if (err != EDPVS_OK) { + RTE_LOG(WARNING, IFA, "%s: fail to get inet maddresses -- %s!\n", + __func__, dpvs_strerror(err)); + return err; + } - if (idev) idev_put(idev); - if (array) { - array->ops = INET_ADDR_GET; - array->ops_flags = param->ifa_ops_flags; + *out = marray; + *outsize = len; + break; + default: + *out = NULL; + *outsize = 0; + return EDPVS_NOTSUPP; } - - *out = array; - *outsize = len; - return EDPVS_OK; } @@ -2023,6 +2271,13 @@ static struct dpvs_msg_type ifa_msg_types[] = { //.cid = rte_get_main_lcore(), .unicast_msg_cb = ifa_msg_sync_cb, .multicast_msg_cb = NULL + }, + { + .type = MSG_TYPE_IFA_IDEVINIT, + .prio = MSG_PRIO_NORM, + .mode = DPVS_MSG_MULTICAST, + .unicast_msg_cb = ifa_msg_idevinit_cb, + .multicast_msg_cb = NULL, } }; @@ -2044,7 +2299,7 @@ static struct dpvs_sockopts ifa_sockopts = { .set_opt_max = SOCKOPT_SET_IFADDR_FLUSH, .set = ifa_sockopt_set, .get_opt_min = SOCKOPT_GET_IFADDR_SHOW, - .get_opt_max = SOCKOPT_GET_IFADDR_SHOW, + .get_opt_max = SOCKOPT_GET_IFMADDR_SHOW, .get = ifa_sockopt_get, }; diff --git a/src/ip_gre.c b/src/ip_gre.c index 43fd31b8b..dbfdad7c7 100644 --- a/src/ip_gre.c +++ b/src/ip_gre.c @@ -233,12 +233,13 @@ static int gre_dev_init(struct netif_port *dev) tnl->hlen = gre_calc_hlen(tnl->params.o_flags); - return EDPVS_OK; + return ip_tunnel_dev_init(dev); } static struct netif_ops gre_dev_ops = { .op_init = gre_dev_init, .op_xmit = gre_xmit, + .op_set_mc_list = ip_tunnel_set_mc_list, .op_get_link = ip_tunnel_get_link, .op_get_stats = ip_tunnel_get_stats, .op_get_promisc = ip_tunnel_get_promisc, diff --git a/src/ip_tunnel.c b/src/ip_tunnel.c index 8b6cd9668..e26f3eee7 100644 --- a/src/ip_tunnel.c +++ b/src/ip_tunnel.c @@ -171,7 +171,8 @@ static struct netif_port *tunnel_create(struct ip_tunnel_tab *tab, if (!strlen(params.ifname)) snprintf(params.ifname, IFNAMSIZ, "%s%%d", ops->kind); - dev = netif_alloc(ops->priv_size, params.ifname, 1, 1, ops->setup); + dev = netif_alloc(NETIF_PORT_ID_INVALID, ops->priv_size, params.ifname, + 1, 1, ops->setup); if (!dev) return NULL; @@ -204,6 +205,9 @@ static struct netif_port *tunnel_create(struct ip_tunnel_tab *tab, dev->flag &= ~NETIF_PORT_FLAG_TX_IP_CSUM_OFFLOAD; dev->flag &= ~NETIF_PORT_FLAG_TX_TCP_CSUM_OFFLOAD; dev->flag &= ~NETIF_PORT_FLAG_TX_UDP_CSUM_OFFLOAD; + dev->flag &= ~NETIF_PORT_FLAG_LLDP; + + dev->in_ptr->flags |= IDEV_F_NO_IPV6; err = netif_port_register(dev); if (err != EDPVS_OK) { @@ -898,6 +902,30 @@ int ip_tunnel_pull_header(struct rte_mbuf *mbuf, int hlen, __be16 in_proto) return EDPVS_OK; } +int ip_tunnel_dev_init(struct netif_port *dev) +{ + int err; + struct ip_tunnel *tnl = netif_priv(dev); + struct inet_device *idev = dev_get_idev(tnl->dev); + + err = idev_addr_init(idev); + if (err != EDPVS_OK) { + idev_put(idev); + return err; + } + + idev_put(idev); + return EDPVS_OK; +} + +int ip_tunnel_set_mc_list(struct netif_port *dev) +{ + // IP tunnel devices need no hw multicast address, + // and should always return success + + return EDPVS_OK; +} + int ip_tunnel_get_link(struct netif_port *dev, struct rte_eth_link *link) { struct ip_tunnel *tnl = netif_priv(dev); diff --git a/src/ipip.c b/src/ipip.c index 8b25f0c8e..8a7ffe100 100644 --- a/src/ipip.c +++ b/src/ipip.c @@ -46,7 +46,9 @@ static int ipip_xmit(struct rte_mbuf *mbuf, struct netif_port *dev) } static struct netif_ops ipip_dev_ops = { + .op_init = ip_tunnel_dev_init, .op_xmit = ipip_xmit, + .op_set_mc_list = ip_tunnel_set_mc_list, .op_get_link = ip_tunnel_get_link, .op_get_stats = ip_tunnel_get_stats, .op_get_promisc = ip_tunnel_get_promisc, diff --git a/src/ipset/ipset_bitmap.c b/src/ipset/ipset_bitmap.c index eaa6d376e..63a554635 100644 --- a/src/ipset/ipset_bitmap.c +++ b/src/ipset/ipset_bitmap.c @@ -32,7 +32,7 @@ bitmap_add(struct ipset *set, void *value, uint16_t flag) /* To avoid same IP, different MAC or other elements */ if (ret || test_bit(e->id, map->members)) { - if (flag & IPSET_F_FORCE) + if (flag & IPSET_F_FORCE) return EDPVS_OK; return EDPVS_EXIST; } @@ -51,8 +51,11 @@ bitmap_del(struct ipset *set, void *value, uint16_t flag) if (e->id >= map->elements) return EDPVS_INVAL; - if (!do(del, value, map)) + if (!do(del, value, map)) { + if (flag & IPSET_F_FORCE) + return EDPVS_OK; return EDPVS_NOTEXIST; + } set->elements--; return EDPVS_OK; @@ -70,7 +73,11 @@ bitmap_test(struct ipset *set, void *value, uint16_t flag) return do(test, value, map, set->dsize); } -ipset_adtfn bitmap_adtfn[IPSET_ADT_MAX] = { bitmap_add, bitmap_del, bitmap_test }; +ipset_adtfn bitmap_adtfn[IPSET_ADT_MAX] = { + [ IPSET_OP_ADD ] = bitmap_add, + [ IPSET_OP_DEL ] = bitmap_del, + [ IPSET_OP_TEST ] = bitmap_test +}; void bitmap_flush(struct ipset *set) diff --git a/src/ipset/ipset_core.c b/src/ipset/ipset_core.c index 2d752be41..85d4325ff 100644 --- a/src/ipset/ipset_core.c +++ b/src/ipset/ipset_core.c @@ -227,6 +227,9 @@ ipset_flush_lcore(void *arg) int i; struct ipset *set; + if (rte_lcore_id() >= DPVS_MAX_LCORE) + return EDPVS_OK; + for (i = 0; i < IPSETS_TBL_SIZE; i++) { list_for_each_entry(set, &this_ipsets_tbl[i], list) set->type->destroy(set); @@ -244,8 +247,12 @@ static int ipset_lcore_init(void *arg) { int i; + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; - if (!rte_lcore_is_enabled(rte_lcore_id())) + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; this_ipsets_tbl = rte_zmalloc(NULL, diff --git a/src/ipset/ipset_hash.c b/src/ipset/ipset_hash.c index e4b93e18f..07c35eaad 100644 --- a/src/ipset/ipset_hash.c +++ b/src/ipset/ipset_hash.c @@ -160,6 +160,8 @@ hash_del(struct ipset *set, void *value, uint16_t flag) return EDPVS_OK; } } + if (flag & IPSET_F_FORCE) + return EDPVS_OK; return EDPVS_NOTEXIST; } @@ -245,7 +247,11 @@ hash_test(struct ipset *set, void *value, uint16_t flag) return 0; } -ipset_adtfn hash_adtfn[IPSET_ADT_MAX] = { hash_add, hash_del, hash_test }; +ipset_adtfn hash_adtfn[IPSET_ADT_MAX] = { + [ IPSET_OP_ADD ] = hash_add, + [ IPSET_OP_DEL ] = hash_del, + [ IPSET_OP_TEST ] = hash_test +}; void hash_flush(struct ipset *set) diff --git a/src/ipset/ipset_hash_ip.c b/src/ipset/ipset_hash_ip.c index 4e114cc5a..b0bc670fb 100644 --- a/src/ipset/ipset_hash_ip.c +++ b/src/ipset/ipset_hash_ip.c @@ -188,14 +188,14 @@ static int hash_ip_create(struct ipset *set, struct ipset_param *param) { hash_create(set, param); - if (param->option.family == AF_INET) { - set->dsize = sizeof(elem4_t); - set->hash_len = offsetof(elem4_t, comment); - set->variant = &hash_ip_variant4; - } else { + if (param->option.family == AF_INET6) { set->dsize = sizeof(elem6_t); set->hash_len = offsetof(elem6_t, comment); set->variant = &hash_ip_variant6; + } else { + set->dsize = sizeof(elem4_t); + set->hash_len = offsetof(elem4_t, comment); + set->variant = &hash_ip_variant4; } return EDPVS_OK; diff --git a/src/ipset/ipset_hash_ipport.c b/src/ipset/ipset_hash_ipport.c index f94ce34a5..ebf39a5b8 100644 --- a/src/ipset/ipset_hash_ipport.c +++ b/src/ipset/ipset_hash_ipport.c @@ -268,14 +268,14 @@ hash_ipport_create(struct ipset *set, struct ipset_param *param) { hash_create(set, param); - if (param->option.family == AF_INET) { - set->dsize = sizeof(elem4_t); - set->hash_len = offsetof(elem4_t, comment); - set->variant = &hash_ipport_variant4; - } else { + if (param->option.family == AF_INET6) { set->dsize = sizeof(elem6_t); set->hash_len = offsetof(elem6_t, comment); set->variant = &hash_ipport_variant6; + } else { + set->dsize = sizeof(elem4_t); + set->hash_len = offsetof(elem4_t, comment); + set->variant = &hash_ipport_variant4; } return EDPVS_OK; diff --git a/src/ipset/ipset_hash_ipportip.c b/src/ipset/ipset_hash_ipportip.c index 87234e4e7..64823550e 100644 --- a/src/ipset/ipset_hash_ipportip.c +++ b/src/ipset/ipset_hash_ipportip.c @@ -284,14 +284,14 @@ hash_ipportip_create(struct ipset *set, struct ipset_param *param) { hash_create(set, param); - if (param->option.family == AF_INET) { - set->dsize = sizeof(elem4_t); - set->hash_len = offsetof(elem4_t, comment); - set->variant = &hash_ipportip_variant4; - } else { + if (param->option.family == AF_INET6) { set->dsize = sizeof(elem6_t); set->hash_len = offsetof(elem6_t, comment); set->variant = &hash_ipportip_variant6; + } else { + set->dsize = sizeof(elem4_t); + set->hash_len = offsetof(elem4_t, comment); + set->variant = &hash_ipportip_variant4; } return EDPVS_OK; diff --git a/src/ipset/ipset_hash_ipportnet.c b/src/ipset/ipset_hash_ipportnet.c index 0c040e7da..78a42008d 100644 --- a/src/ipset/ipset_hash_ipportnet.c +++ b/src/ipset/ipset_hash_ipportnet.c @@ -285,10 +285,10 @@ hash_ipportnet_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, comment); - if (param->option.family == AF_INET) - set->variant = &hash_ipportnet_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_ipportnet_variant6; + else + set->variant = &hash_ipportnet_variant4; return EDPVS_OK; } diff --git a/src/ipset/ipset_hash_net.c b/src/ipset/ipset_hash_net.c index 7bbee349d..fb9084c32 100644 --- a/src/ipset/ipset_hash_net.c +++ b/src/ipset/ipset_hash_net.c @@ -219,10 +219,10 @@ hash_net_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, comment); - if (param->option.family == AF_INET) - set->variant = &hash_net_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_net_variant6; + else + set->variant = &hash_net_variant4; return EDPVS_OK; } diff --git a/src/ipset/ipset_hash_netport.c b/src/ipset/ipset_hash_netport.c index 975eef330..fb17917d4 100644 --- a/src/ipset/ipset_hash_netport.c +++ b/src/ipset/ipset_hash_netport.c @@ -269,10 +269,10 @@ hash_netport_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, comment); - if (param->option.family == AF_INET) - set->variant = &hash_netport_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_netport_variant6; + else + set->variant = &hash_netport_variant4; return EDPVS_OK; } diff --git a/src/ipset/ipset_hash_netportiface.c b/src/ipset/ipset_hash_netportiface.c index 7b15c4bb5..ef00c5d32 100644 --- a/src/ipset/ipset_hash_netportiface.c +++ b/src/ipset/ipset_hash_netportiface.c @@ -284,10 +284,10 @@ hash_netportiface_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, dev); - if (param->option.family == AF_INET) - set->variant = &hash_netportiface_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_netportiface_variant6; + else + set->variant = &hash_netportiface_variant4; return EDPVS_OK; } diff --git a/src/ipset/ipset_hash_netportnet.c b/src/ipset/ipset_hash_netportnet.c index 29dedc4f6..16b14ec55 100644 --- a/src/ipset/ipset_hash_netportnet.c +++ b/src/ipset/ipset_hash_netportnet.c @@ -287,10 +287,10 @@ hash_netportnet_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, comment); - if (param->option.family == AF_INET) - set->variant = &hash_netportnet_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_netportnet_variant6; + else + set->variant = &hash_netportnet_variant4; return EDPVS_OK; } diff --git a/src/ipset/ipset_hash_netportnetport.c b/src/ipset/ipset_hash_netportnetport.c index 5849c3d46..2cf0090a5 100644 --- a/src/ipset/ipset_hash_netportnetport.c +++ b/src/ipset/ipset_hash_netportnetport.c @@ -299,10 +299,10 @@ hash_netportnetport_create(struct ipset *set, struct ipset_param *param) set->dsize = sizeof(elem_t); set->hash_len = offsetof(elem_t, comment); - if (param->option.family == AF_INET) - set->variant = &hash_netportnetport_variant4; - else + if (param->option.family == AF_INET6) set->variant = &hash_netportnetport_variant6; + else + set->variant = &hash_netportnetport_variant4; return EDPVS_OK; } diff --git a/src/ipv6/ipv6.c b/src/ipv6/ipv6.c index 1fa712110..37da08fb6 100644 --- a/src/ipv6/ipv6.c +++ b/src/ipv6/ipv6.c @@ -46,8 +46,12 @@ static rte_rwlock_t inet6_prot_lock; /* * IPv6 configures with default values. */ -static bool conf_ipv6_forwarding = false; -static bool conf_ipv6_disable = false; +static struct ipv6_config ip6_configs; + +const struct ipv6_config *ip6_config_get(void) +{ + return &ip6_configs; +}; /* * IPv6 statistics @@ -108,42 +112,109 @@ static void ip6_prot_init(void) rte_rwlock_write_unlock(&inet6_prot_lock); } -static void ip6_conf_forward(vector_t tokens) +static void ip6_forwarding_handler(vector_t tokens) { char *str = set_value(tokens); assert(str); if (strcasecmp(str, "on") == 0) - conf_ipv6_forwarding = true; + ip6_configs.forwarding = 1; else if (strcasecmp(str, "off") == 0) - conf_ipv6_forwarding = false; + ip6_configs.forwarding = 0; else RTE_LOG(WARNING, IPV6, "invalid ipv6:forwarding %s\n", str); - RTE_LOG(INFO, IPV6, "ipv6:forwarding = %s\n", conf_ipv6_forwarding ? "on" : "off"); + RTE_LOG(INFO, IPV6, "ipv6:forwarding = %s\n", ip6_configs.forwarding ? "on" : "off"); FREE_PTR(str); } -static void ip6_conf_disable(vector_t tokens) +static void ip6_disable_handler(vector_t tokens) { char *str = set_value(tokens); assert(str); if (strcasecmp(str, "on") == 0) - conf_ipv6_disable = true; + ip6_configs.disable = 1; else if (strcasecmp(str, "off") == 0) - conf_ipv6_disable = false; + ip6_configs.disable = 0; else RTE_LOG(WARNING, IPV6, "invalid ipv6:disable %s\n", str); - RTE_LOG(INFO, IPV6, "ipv6:disable = %s\n", conf_ipv6_disable ? "on" : "off"); + RTE_LOG(INFO, IPV6, "ipv6:disable=%s\n", ip6_configs.disable ? "disabled" : "enabled"); + + FREE_PTR(str); +} + +static void ip6_addr_gen_mode_handler(vector_t tokens) +{ + char *str = set_value(tokens); + + assert(str); + + if (!strcasecmp(str, "eui64")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_EUI64; + else if (!strcasecmp(str, "none")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_NONE; + else if (!strcasecmp(str, "stable-privacy")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_STABLE_PRIVACY; + else if (!strcasecmp(str, "random")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_RANDOM; + else + RTE_LOG(WARNING, IPV6, "invalid ipv6:addr_gen_mode:%s\n", str); + + RTE_LOG(INFO, IPV6, "ipv6:addr_gen_mode=%s\n", str); + + FREE_PTR(str); +} + +static void ip6_stable_secret_handler(vector_t tokens) +{ + bool valid = true; + size_t i, len; + char *str = set_value(tokens); + + assert(str); + len = strlen(str); + if (len < 32) { + valid = false; + } else { + for (i = 0; i < 32; i++) { + if (!isxdigit(str[i])) { + valid = false; + break; + } + } + } + if (!valid) { + RTE_LOG(WARNING, IPV6, "invalid ipv6:stable_secret %s, " + "a 128-bit hexadecimal string required\n", str); + FREE_PTR(str); + return; + } + + if (hexstr2binary(str, 32, (uint8_t *)(&ip6_configs.secret_stable.secret), 16) == 16) + ip6_configs.secret_stable.initialized = true; + else + RTE_LOG(WARNING, IPV6, "fail to tranlate ipv6:stable_secret %s into binary\n", str); + RTE_LOG(INFO, IPV6, "ipv6:stable_secret configured"); FREE_PTR(str); } +static inline void ip6_gen_mode_random_init(void) +{ + const char hex_chars[] = "0123456789abcdef"; + char *buf = (char *)(&ip6_configs.secret_random.secret); + int i; + + for (i = 0; i < 16; i++) + buf[i] = hex_chars[random() % 16]; + ip6_configs.secret_random.initialized = true; +} + /* refer linux:ip6_input_finish() */ static int ip6_local_in_fin(struct rte_mbuf *mbuf) { @@ -371,7 +442,7 @@ int ip6_output(struct rte_mbuf *mbuf) mbuf->port = dev->id; iftraf_pkt_out(AF_INET6, mbuf, dev); - if (unlikely(conf_ipv6_disable)) { + if (unlikely(ip6_configs.disable)) { IP6_INC_STATS(outdiscards); if (rt) route6_put(rt); @@ -411,7 +482,7 @@ static int ip6_forward(struct rte_mbuf *mbuf) int addrtype; uint32_t mtu; - if (!conf_ipv6_forwarding) + if (!ip6_configs.forwarding) goto error; if (mbuf->packet_type != ETH_PKT_HOST) @@ -539,7 +610,7 @@ static int ip6_rcv(struct rte_mbuf *mbuf, struct netif_port *dev) IP6_UPD_PO_STATS(in, mbuf->pkt_len); iftraf_pkt_in(AF_INET6, mbuf, dev); - if (unlikely(conf_ipv6_disable)) { + if (unlikely(ip6_configs.disable)) { IP6_INC_STATS(indiscards); goto drop; } @@ -656,6 +727,8 @@ int ipv6_init(void) /* htons, cpu_to_be16 not work when struct initialization :( */ ip6_pkt_type.type = htons(RTE_ETHER_TYPE_IPV6); + ip6_gen_mode_random_init(); + err = netif_register_pkt(&ip6_pkt_type); if (err) goto reg_pkt_err; @@ -815,8 +888,10 @@ void ipv6_keyword_value_init(void) /* KW_TYPE_INIT keyword */ } /* KW_TYPE NORMAL keyword */ - conf_ipv6_forwarding = false; - conf_ipv6_disable = false; + ip6_configs.forwarding = 0; + ip6_configs.disable = 0; + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_EUI64; + ip6_configs.secret_stable.initialized = false; route6_keyword_value_init(); } @@ -824,8 +899,10 @@ void ipv6_keyword_value_init(void) void install_ipv6_keywords(void) { install_keyword_root("ipv6_defs", NULL); - install_keyword("forwarding", ip6_conf_forward, KW_TYPE_NORMAL); - install_keyword("disable", ip6_conf_disable, KW_TYPE_NORMAL); + install_keyword("forwarding", ip6_forwarding_handler, KW_TYPE_NORMAL); + install_keyword("disable", ip6_disable_handler, KW_TYPE_NORMAL); + install_keyword("addr_gen_mode", ip6_addr_gen_mode_handler, KW_TYPE_NORMAL); + install_keyword("stable_secret", ip6_stable_secret_handler, KW_TYPE_NORMAL); install_route6_keywords(); } diff --git a/src/ipv6/route6.c b/src/ipv6/route6.c index 0c3d06d79..311e9f64c 100644 --- a/src/ipv6/route6.c +++ b/src/ipv6/route6.c @@ -135,10 +135,14 @@ static int rt6_setup_lcore(void *arg) int err; bool global; struct timeval tv; + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; tv.tv_sec = g_rt6_recycle_time, tv.tv_usec = 0, - global = (rte_lcore_id() == rte_get_main_lcore()); + global = (cid == rte_get_main_lcore()); INIT_LIST_HEAD(&this_rt6_dustbin.routes); err = dpvs_timer_sched_period(&this_rt6_dustbin.tm, &tv, rt6_recycle, NULL, global); @@ -152,6 +156,9 @@ static int rt6_destroy_lcore(void *arg) { struct route6 *rt6, *next; + if (rte_lcore_id() >= DPVS_MAX_LCORE) + return EDPVS_OK; + list_for_each_entry_safe(rt6, next, &this_rt6_dustbin.routes, hnode) { if (rte_atomic32_read(&rt6->refcnt) <= 1) { /* need judge refcnt here? */ list_del(&rt6->hnode); diff --git a/src/ipvs/ip_vs_blklst.c b/src/ipvs/ip_vs_blklst.c index 0afebc6cb..a608cd0c5 100644 --- a/src/ipvs/ip_vs_blklst.c +++ b/src/ipvs/ip_vs_blklst.c @@ -35,86 +35,198 @@ * */ -#define DPVS_BLKLST_TAB_BITS 16 -#define DPVS_BLKLST_TAB_SIZE (1 << DPVS_BLKLST_TAB_BITS) -#define DPVS_BLKLST_TAB_MASK (DPVS_BLKLST_TAB_SIZE - 1) - -#define this_blklst_tab (RTE_PER_LCORE(dp_vs_blklst_tab)) -#define this_num_blklsts (RTE_PER_LCORE(num_blklsts)) +#define DPVS_BLKLST_TAB_BITS 16 +#define DPVS_BLKLST_TAB_SIZE (1 << DPVS_BLKLST_TAB_BITS) +#define DPVS_BLKLST_TAB_MASK (DPVS_BLKLST_TAB_SIZE - 1) +#define this_blklst_tab (RTE_PER_LCORE(dp_vs_blklst_tab)) +#define this_num_blklsts (RTE_PER_LCORE(num_blklsts)) + +#define DPVS_BLKLST_IPSET_TAB_BITS 8 +#define DPVS_BLKLST_IPSET_TAB_SIZE (1 << DPVS_BLKLST_IPSET_TAB_BITS) +#define DPVS_BLKLST_IPSET_TAB_MASK (DPVS_BLKLST_IPSET_TAB_SIZE - 1) +#define this_blklst_ipset_tab (RTE_PER_LCORE(dp_vs_blklst_ipset_tab)) +#define this_num_blklsts_ipset (RTE_PER_LCORE(num_blklsts_ipset)) static RTE_DEFINE_PER_LCORE(struct list_head *, dp_vs_blklst_tab); -static RTE_DEFINE_PER_LCORE(rte_atomic32_t, num_blklsts); +static RTE_DEFINE_PER_LCORE(uint32_t, num_blklsts); + +static RTE_DEFINE_PER_LCORE(struct list_head *, dp_vs_blklst_ipset_tab); +static RTE_DEFINE_PER_LCORE(uint32_t, num_blklsts_ipset); static uint32_t dp_vs_blklst_rnd; +static inline void blklst_fill_conf(const struct blklst_entry *entry, + struct dp_vs_blklst_conf *conf) +{ + memset(conf, 0, sizeof(*conf)); + conf->vaddr = entry->vaddr; + conf->vport = entry->vport; + conf->proto = entry->proto; + conf->af = entry->af; + conf->subject = entry->subject; + if (entry->set) + strncpy(conf->ipset, entry->set->name, sizeof(conf->ipset) - 1); +} + static inline uint32_t blklst_hashkey(const union inet_addr *vaddr, - const union inet_addr *blklst) + uint16_t vport, const union inet_addr *subject, bool ipset) { /* jhash hurts performance, we do not use rte_jhash_2words here */ - return ((rte_be_to_cpu_32(vaddr->in.s_addr) * 31 - + rte_be_to_cpu_32(blklst->in.s_addr)) * 31 - + dp_vs_blklst_rnd) & DPVS_BLKLST_TAB_MASK; + if (ipset) + return ((((vaddr->in.s_addr * 31) ^ vport) * 131) + ^ dp_vs_blklst_rnd) & DPVS_BLKLST_IPSET_TAB_MASK; + + return ((((vaddr->in.s_addr * 31) ^ subject->in.s_addr) * 131) + ^ (vport ^ dp_vs_blklst_rnd)) & DPVS_BLKLST_TAB_MASK; } -struct blklst_entry *dp_vs_blklst_lookup(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst) +static inline struct blklst_entry *dp_vs_blklst_ip_lookup( + int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject) { unsigned hashkey; - struct blklst_entry *blklst_node; - - hashkey = blklst_hashkey(vaddr, blklst); - list_for_each_entry(blklst_node, &this_blklst_tab[hashkey], list) { - if (blklst_node->af == af && blklst_node->proto == proto && - blklst_node->vport == vport && - inet_addr_equal(af, &blklst_node->vaddr, vaddr) && - inet_addr_equal(af, &blklst_node->blklst, blklst)) - return blklst_node; + struct blklst_entry *entry; + + hashkey = blklst_hashkey(vaddr, vport, subject, false); + list_for_each_entry(entry, &this_blklst_tab[hashkey], list) { + if (entry->af == af && entry->proto == proto && + entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr) && + inet_addr_equal(af, &entry->subject, subject)) + return entry; } + return NULL; } -static int dp_vs_blklst_add_lcore(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst) +static inline struct blklst_entry *dp_vs_blklst_ipset_lookup( + int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const char *ipset) { unsigned hashkey; - struct blklst_entry *new, *blklst_node; + struct blklst_entry *entry; - blklst_node = dp_vs_blklst_lookup(af, proto, vaddr, vport, blklst); - if (blklst_node) { - return EDPVS_EXIST; + hashkey = blklst_hashkey(vaddr, vport, NULL, true); + list_for_each_entry(entry, &this_blklst_ipset_tab[hashkey], list) { + if (entry->af == af && entry->proto == proto && entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr) && + !strncmp(entry->set->name, ipset, sizeof(entry->set->name))) + return entry; + } + + return NULL; +} + +static bool dp_vs_blklst_ip_match_set(int af, uint8_t proto, + const union inet_addr *vaddr, uint16_t vport, + struct rte_mbuf *mbuf) +{ + bool res = false; + unsigned hashkey; + struct blklst_entry *entry; + + hashkey = blklst_hashkey(vaddr, vport, NULL, true); + list_for_each_entry(entry, &this_blklst_ipset_tab[hashkey], list) { + if (entry->af == af && entry->proto == proto && + entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr)) { + rte_pktmbuf_prepend(mbuf, mbuf->l2_len); + res = elem_in_set(entry->set, mbuf, entry->dst_match); + rte_pktmbuf_adj(mbuf, mbuf->l2_len); + if (res) + break; + } } + return res; +} - hashkey = blklst_hashkey(vaddr, blklst); +static struct blklst_entry *dp_vs_blklst_lookup(const struct dp_vs_blklst_conf *conf) +{ + struct blklst_entry *entry; + + entry = dp_vs_blklst_ip_lookup(conf->af, conf->proto, &conf->vaddr, conf->vport, + &conf->subject); + if (entry) + return entry; + + return dp_vs_blklst_ipset_lookup(conf->af, conf->proto, &conf->vaddr, conf->vport, + conf->ipset); +} + +bool dp_vs_blklst_filtered(int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject, struct rte_mbuf *mbuf) +{ + if (dp_vs_blklst_ip_lookup(af, proto, vaddr, vport, subject)) + return true; + + return dp_vs_blklst_ip_match_set(af, proto, vaddr, vport, mbuf); +} + +static int dp_vs_blklst_add_lcore(const struct dp_vs_blklst_conf *conf) +{ + unsigned hashkey; + struct blklst_entry *new; + bool is_ipset = conf->ipset[0] != '\0'; + + if (dp_vs_blklst_lookup(conf)) + return EDPVS_EXIST; + + hashkey = blklst_hashkey(&conf->vaddr, conf->vport, &conf->subject, is_ipset); new = rte_zmalloc("new_blklst_entry", sizeof(struct blklst_entry), 0); if (unlikely(new == NULL)) return EDPVS_NOMEM; - new->af = af; - new->proto = proto; - new->vport = vport; - memcpy(&new->vaddr, vaddr, sizeof(union inet_addr)); - memcpy(&new->blklst, blklst, sizeof(union inet_addr)); - list_add(&new->list, &this_blklst_tab[hashkey]); - rte_atomic32_inc(&this_num_blklsts); + new->vaddr = conf->vaddr; + new->vport = conf->vport; + new->proto = conf->proto; + new->af = conf->af; + + if (is_ipset) { + new->set = ipset_get(conf->ipset); + if (!new->set) { + RTE_LOG(ERR, SERVICE, "[%2d] %s: ipset %s not found\n", + rte_lcore_id(), __func__, conf->ipset); + rte_free(new); + return EDPVS_INVAL; + } + // Notes: Reassess it when new ipset types added! + if (!strcmp(new->set->type->name, "hash:ip,port,net") || + !strcmp(new->set->type->name, "hash:ip,port,ip") || + !strcmp(new->set->type->name, "hash:net,port,net")) + new->dst_match = true; + else + new->dst_match = false; + list_add(&new->list, &this_blklst_ipset_tab[hashkey]); + ++this_num_blklsts_ipset; + } else { + new->subject = conf->subject; + list_add(&new->list, &this_blklst_tab[hashkey]); + ++this_num_blklsts; + } return EDPVS_OK; } -static int dp_vs_blklst_del_lcore(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst) +static int dp_vs_blklst_del_lcore(const struct dp_vs_blklst_conf *conf) { - struct blklst_entry *blklst_node; + struct blklst_entry *entry; - blklst_node = dp_vs_blklst_lookup(af, proto, vaddr, vport, blklst); - if (blklst_node != NULL) { - list_del(&blklst_node->list); - rte_free(blklst_node); - rte_atomic32_dec(&this_num_blklsts); - return EDPVS_OK; + entry = dp_vs_blklst_lookup(conf); + if (!entry) + return EDPVS_NOTEXIST; + + if (entry->set) { /* ipset entry */ + list_del(&entry->list); + ipset_put(entry->set); + --this_num_blklsts_ipset; + } else { /* ip entry */ + list_del(&entry->list); + --this_num_blklsts; } - return EDPVS_NOTEXIST; + rte_free(entry); + return EDPVS_OK; } static uint32_t blklst_msg_seq(void) @@ -123,42 +235,33 @@ static uint32_t blklst_msg_seq(void) return counter++; } -static int dp_vs_blklst_add(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst) +static int dp_vs_blklst_add(const struct dp_vs_blklst_conf *conf) { lcoreid_t cid = rte_lcore_id(); int err; struct dpvs_msg *msg; - struct dp_vs_blklst_conf cf; if (cid != rte_get_main_lcore()) { RTE_LOG(INFO, SERVICE, "%s must set from master lcore\n", __func__); return EDPVS_NOTSUPP; } - memset(&cf, 0, sizeof(struct dp_vs_blklst_conf)); - memcpy(&(cf.vaddr), vaddr,sizeof(union inet_addr)); - memcpy(&(cf.blklst), blklst, sizeof(union inet_addr)); - cf.af = af; - cf.vport = vport; - cf.proto = proto; - - /*set blklst ip on master lcore*/ - err = dp_vs_blklst_add_lcore(af, proto, vaddr, vport, blklst); + /* master lcore */ + err = dp_vs_blklst_add_lcore(conf); if (err && err != EDPVS_EXIST) { - RTE_LOG(ERR, SERVICE, "[%s] fail to set blklst ip -- %s\n", __func__, dpvs_strerror(err)); + RTE_LOG(ERR, SERVICE, "%s: fail to add blklst entry -- %s\n", __func__, dpvs_strerror(err)); return err; } - /*set blklst ip on all slave lcores*/ + /* slave lcores */ msg = msg_make(MSG_TYPE_BLKLST_ADD, blklst_msg_seq(), DPVS_MSG_MULTICAST, - cid, sizeof(struct dp_vs_blklst_conf), &cf); + cid, sizeof(struct dp_vs_blklst_conf), conf); if (unlikely(!msg)) return EDPVS_NOMEM; err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL); if (err != EDPVS_OK) { msg_destroy(&msg); - RTE_LOG(INFO, SERVICE, "[%s] fail to send multicast message\n", __func__); + RTE_LOG(INFO, SERVICE, "%s: fail to send multicast message\n", __func__); return err; } msg_destroy(&msg); @@ -166,35 +269,27 @@ static int dp_vs_blklst_add(int af, uint8_t proto, const union inet_addr *vaddr, return EDPVS_OK; } -static int dp_vs_blklst_del(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *blklst) +static int dp_vs_blklst_del(const struct dp_vs_blklst_conf *conf) { lcoreid_t cid = rte_lcore_id(); int err; struct dpvs_msg *msg; - struct dp_vs_blklst_conf cf; if (cid != rte_get_main_lcore()) { RTE_LOG(INFO, SERVICE, "%s must set from master lcore\n", __func__); return EDPVS_NOTSUPP; } - memset(&cf, 0, sizeof(struct dp_vs_blklst_conf)); - memcpy(&(cf.vaddr), vaddr,sizeof(union inet_addr)); - memcpy(&(cf.blklst), blklst, sizeof(union inet_addr)); - cf.af = af; - cf.vport = vport; - cf.proto = proto; - - /*del blklst ip on master lcores*/ - err = dp_vs_blklst_del_lcore(af, proto, vaddr, vport, blklst); + /* master lcore */ + err = dp_vs_blklst_del_lcore(conf); if (err) { + RTE_LOG(ERR, SERVICE, "%s: fail to del blklst entry -- %s\n", __func__, dpvs_strerror(err)); return err; } - /*del blklst ip on all slave lcores*/ + /* slave lcores */ msg = msg_make(MSG_TYPE_BLKLST_DEL, blklst_msg_seq(), DPVS_MSG_MULTICAST, - cid, sizeof(struct dp_vs_blklst_conf), &cf); + cid, sizeof(struct dp_vs_blklst_conf), conf); if (!msg) return EDPVS_NOMEM; err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL); @@ -212,32 +307,52 @@ void dp_vs_blklst_flush(struct dp_vs_service *svc) { int hash; struct blklst_entry *entry, *next; + struct dp_vs_blklst_conf conf; for (hash = 0; hash < DPVS_BLKLST_TAB_SIZE; hash++) { list_for_each_entry_safe(entry, next, &this_blklst_tab[hash], list) { if (entry->af == svc->af - && entry->vport == svc->port - && entry->proto == svc->proto - && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) - dp_vs_blklst_del(svc->af, entry->proto, &entry->vaddr, - entry->vport, &entry->blklst); + && entry->vport == svc->port + && entry->proto == svc->proto + && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) { + blklst_fill_conf(entry, &conf); + dp_vs_blklst_del(&conf); + } + } + } + + for (hash = 0; hash < DPVS_BLKLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry_safe(entry, next, &this_blklst_ipset_tab[hash], list) { + if (entry->af == svc->af + && entry->vport == svc->port + && entry->proto == svc->proto + && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) { + blklst_fill_conf(entry, &conf); + dp_vs_blklst_del(&conf); + } } } - return; } static void dp_vs_blklst_flush_all(void) { - struct blklst_entry *entry, *next; int hash; + struct blklst_entry *entry, *next; + struct dp_vs_blklst_conf conf; for (hash = 0; hash < DPVS_BLKLST_TAB_SIZE; hash++) { list_for_each_entry_safe(entry, next, &this_blklst_tab[hash], list) { - dp_vs_blklst_del(entry->af, entry->proto, &entry->vaddr, - entry->vport, &entry->blklst); + blklst_fill_conf(entry, &conf); + dp_vs_blklst_del(&conf); + } + } + + for (hash = 0; hash < DPVS_BLKLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry_safe(entry, next, &this_blklst_ipset_tab[hash], list) { + blklst_fill_conf(entry, &conf); + dp_vs_blklst_del(&conf); } } - return; } /* @@ -245,40 +360,19 @@ static void dp_vs_blklst_flush_all(void) */ static int blklst_sockopt_set(sockoptid_t opt, const void *conf, size_t size) { - const struct dp_vs_blklst_conf *blklst_conf = conf; - int err; - - if (!conf && size < sizeof(*blklst_conf)) + if (!conf && size < sizeof(struct dp_vs_blklst_conf)) return EDPVS_INVAL; switch (opt) { - case SOCKOPT_SET_BLKLST_ADD: - err = dp_vs_blklst_add(blklst_conf->af, - blklst_conf->proto, &blklst_conf->vaddr, - blklst_conf->vport, &blklst_conf->blklst); - break; - case SOCKOPT_SET_BLKLST_DEL: - err = dp_vs_blklst_del(blklst_conf->af, - blklst_conf->proto, &blklst_conf->vaddr, - blklst_conf->vport, &blklst_conf->blklst); - break; - default: - err = EDPVS_NOTSUPP; - break; + case SOCKOPT_SET_BLKLST_ADD: + return dp_vs_blklst_add(conf); + case SOCKOPT_SET_BLKLST_DEL: + return dp_vs_blklst_del(conf); + default: + return EDPVS_NOTSUPP; } - return err; -} - -static void blklst_fill_conf(struct dp_vs_blklst_conf *cf, - const struct blklst_entry *entry) -{ - memset(cf, 0 ,sizeof(*cf)); - cf->af = entry->af; - cf->vaddr = entry->vaddr; - cf->blklst = entry->blklst; - cf->proto = entry->proto; - cf->vport = entry->vport; + return EDPVS_OK; } static int blklst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, @@ -289,7 +383,7 @@ static int blklst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, size_t naddr, hash; int off = 0; - naddr = rte_atomic32_read(&this_num_blklsts); + naddr = this_num_blklsts + this_num_blklsts_ipset; *outsize = sizeof(struct dp_vs_blklst_conf_array) + naddr * sizeof(struct dp_vs_blklst_conf); *out = rte_calloc(NULL, 1, *outsize, 0); @@ -302,45 +396,52 @@ static int blklst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, list_for_each_entry(entry, &this_blklst_tab[hash], list) { if (off >= naddr) break; - blklst_fill_conf(&array->blklsts[off++], entry); + blklst_fill_conf(entry, &array->blklsts[off++]); + } + } + + for (hash = 0; hash < DPVS_BLKLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry(entry, &this_blklst_ipset_tab[hash], list) { + if (off >= naddr) + break; + blklst_fill_conf(entry, &array->blklsts[off++]); } } return EDPVS_OK; } - static int blklst_msg_process(bool add, struct dpvs_msg *msg) { - struct dp_vs_blklst_conf *cf; + struct dp_vs_blklst_conf *conf; int err; assert(msg); if (msg->len != sizeof(struct dp_vs_blklst_conf)){ - RTE_LOG(ERR, SERVICE, "%s: bad message.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: bad message\n", __func__); return EDPVS_INVAL; } - cf = (struct dp_vs_blklst_conf *)msg->data; + conf = (struct dp_vs_blklst_conf *)msg->data; if (add) { - err = dp_vs_blklst_add_lcore(cf->af, cf->proto, &cf->vaddr, cf->vport, &cf->blklst); - if (err && err != EDPVS_EXIST) { - RTE_LOG(ERR, SERVICE, "%s: fail to add blklst: %s.\n", __func__, dpvs_strerror(err)); - } - } - else { - err = dp_vs_blklst_del_lcore(cf->af, cf->proto, &cf->vaddr, cf->vport, &cf->blklst); + err = dp_vs_blklst_add_lcore(conf); + if (err && err != EDPVS_EXIST) + RTE_LOG(ERR, SERVICE, "%s: fail to add blklst: %s\n", __func__, dpvs_strerror(err)); + } else { + err = dp_vs_blklst_del_lcore(conf); + if (err && err != EDPVS_NOTEXIST) + RTE_LOG(ERR, SERVICE, "%s: fail to del blklst: %s\n", __func__, dpvs_strerror(err)); } return err; } -inline static int blklst_add_msg_cb(struct dpvs_msg *msg) +static inline int blklst_add_msg_cb(struct dpvs_msg *msg) { return blklst_msg_process(true, msg); } -inline static int blklst_del_msg_cb(struct dpvs_msg *msg) +static inline int blklst_del_msg_cb(struct dpvs_msg *msg) { return blklst_msg_process(false, msg); } @@ -358,23 +459,45 @@ static struct dpvs_sockopts blklst_sockopts = { static int blklst_lcore_init(void *args) { int i; - if (!rte_lcore_is_enabled(rte_lcore_id())) - return EDPVS_DISABLED; + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) + return EDPVS_DISABLED; + + this_num_blklsts = 0; + this_num_blklsts_ipset = 0; + this_blklst_tab = rte_malloc(NULL, - sizeof(struct list_head) * DPVS_BLKLST_TAB_SIZE, - RTE_CACHE_LINE_SIZE); + sizeof(struct list_head) * DPVS_BLKLST_TAB_SIZE, + RTE_CACHE_LINE_SIZE); if (!this_blklst_tab) return EDPVS_NOMEM; - for (i = 0; i < DPVS_BLKLST_TAB_SIZE; i++) INIT_LIST_HEAD(&this_blklst_tab[i]); + this_blklst_ipset_tab = rte_malloc(NULL, sizeof(struct list_head) * + DPVS_BLKLST_IPSET_TAB_SIZE, RTE_CACHE_LINE_SIZE); + if (!this_blklst_ipset_tab) { + rte_free(this_blklst_tab); + return EDPVS_NOMEM; + } + for (i = 0; i < DPVS_BLKLST_IPSET_TAB_SIZE; i++) + INIT_LIST_HEAD(&this_blklst_ipset_tab[i]); + return EDPVS_OK; } static int blklst_lcore_term(void *args) { - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; dp_vs_blklst_flush_all(); @@ -383,6 +506,12 @@ static int blklst_lcore_term(void *args) rte_free(this_blklst_tab); this_blklst_tab = NULL; } + + if (this_blklst_ipset_tab) { + rte_free(this_blklst_ipset_tab); + this_blklst_ipset_tab = NULL; + } + return EDPVS_OK; } @@ -392,13 +521,11 @@ int dp_vs_blklst_init(void) lcoreid_t cid; struct dpvs_msg_type msg_type; - rte_atomic32_set(&this_num_blklsts, 0); - rte_eal_mp_remote_launch(blklst_lcore_init, NULL, CALL_MAIN); RTE_LCORE_FOREACH_WORKER(cid) { if ((err = rte_eal_wait_lcore(cid)) < 0) { - RTE_LOG(WARNING, SERVICE, "%s: lcore %d: %s.\n", - __func__, cid, dpvs_strerror(err)); + RTE_LOG(WARNING, SERVICE, "[%02d] %s: blklst init failed -- %s\n", + cid, __func__, dpvs_strerror(err)); return err; } } @@ -411,7 +538,8 @@ int dp_vs_blklst_init(void) msg_type.unicast_msg_cb = blklst_add_msg_cb; err = msg_type_mc_register(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to register msg.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: register BLKLST_ADD msg failed -- %s\n", + __func__, dpvs_strerror(err)); return err; } @@ -423,12 +551,17 @@ int dp_vs_blklst_init(void) msg_type.unicast_msg_cb = blklst_del_msg_cb; err = msg_type_mc_register(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to register msg.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: register BLKLST_DEL msg failed -- %s\n", + __func__, dpvs_strerror(err)); return err; } - if ((err = sockopt_register(&blklst_sockopts)) != EDPVS_OK) + if ((err = sockopt_register(&blklst_sockopts)) != EDPVS_OK) { + RTE_LOG(ERR, SERVICE, "%s: register sockopts failed -- %s\n", + __func__, dpvs_strerror(err)); return err; + } + dp_vs_blklst_rnd = (uint32_t)random(); return EDPVS_OK; @@ -438,15 +571,42 @@ int dp_vs_blklst_term(void) { int err; lcoreid_t cid; + struct dpvs_msg_type msg_type; - if ((err = sockopt_unregister(&blklst_sockopts)) != EDPVS_OK) - return err; + if ((err = sockopt_unregister(&blklst_sockopts)) != EDPVS_OK) { + RTE_LOG(WARNING, SERVICE, "%s: unregister sockopts failed -- %s\n", + __func__, dpvs_strerror(err)); + } + + memset(&msg_type, 0, sizeof(struct dpvs_msg_type)); + msg_type.type = MSG_TYPE_BLKLST_DEL; + msg_type.mode = DPVS_MSG_MULTICAST; + msg_type.prio = MSG_PRIO_NORM; + msg_type.cid = rte_lcore_id(); + msg_type.unicast_msg_cb = blklst_del_msg_cb; + err = msg_type_mc_unregister(&msg_type); + if (err != EDPVS_OK) { + RTE_LOG(ERR, SERVICE, "%s: unregister BLKLST_DEL msg failed -- %s\n", + __func__, dpvs_strerror(err)); + } + + memset(&msg_type, 0, sizeof(struct dpvs_msg_type)); + msg_type.type = MSG_TYPE_BLKLST_ADD; + msg_type.mode = DPVS_MSG_MULTICAST; + msg_type.prio = MSG_PRIO_NORM; + msg_type.cid = rte_lcore_id(); + msg_type.unicast_msg_cb = blklst_add_msg_cb; + err = msg_type_mc_unregister(&msg_type); + if (err != EDPVS_OK) { + RTE_LOG(ERR, SERVICE, "%s: unregister BLKLST_ADD msg failed -- %s\n", + __func__, dpvs_strerror(err)); + } rte_eal_mp_remote_launch(blklst_lcore_term, NULL, CALL_MAIN); RTE_LCORE_FOREACH_WORKER(cid) { if ((err = rte_eal_wait_lcore(cid)) < 0) { - RTE_LOG(WARNING, SERVICE, "%s: lcore %d: %s.\n", - __func__, cid, dpvs_strerror(err)); + RTE_LOG(WARNING, SERVICE, "[%02d] %s: blklst termination failed -- %s\n", + cid, __func__, dpvs_strerror(err)); } } diff --git a/src/ipvs/ip_vs_conhash.c b/src/ipvs/ip_vs_conhash.c index 360346ad2..ca68a43b9 100644 --- a/src/ipvs/ip_vs_conhash.c +++ b/src/ipvs/ip_vs_conhash.c @@ -39,6 +39,14 @@ struct conhash_sched_data { /* * QUIC CID hash target for quic* * QUIC CID(qid) should be configured in UDP service + * + * This is an early Google QUIC implementation, and has been obsoleted. + * https://docs.google.com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/edit?pli=1#heading=h.o9jvitkc5d2g + * + * Use IETF QUIC(officially published in 2021) instead. + * Configure `--quic` option on DPVS service to enable it. + * The quic application on RS must conform with the CID format agreement + * declared in `include/ipvs/quic.h`. */ static int get_quic_hash_target(int af, const struct rte_mbuf *mbuf, uint64_t *quic_cid) diff --git a/src/ipvs/ip_vs_conn.c b/src/ipvs/ip_vs_conn.c index e8deb0576..4179e68ad 100644 --- a/src/ipvs/ip_vs_conn.c +++ b/src/ipvs/ip_vs_conn.c @@ -30,6 +30,7 @@ #include "ipvs/synproxy.h" #include "ipvs/proto_tcp.h" #include "ipvs/proto_udp.h" +#include "ipvs/proto_sctp.h" #include "ipvs/proto_icmp.h" #include "parser/parser.h" #include "ctrl.h" @@ -517,7 +518,9 @@ void dp_vs_conn_set_timeout(struct dp_vs_conn *conn, struct dp_vs_proto *pp) /* set proper timeout */ if ((conn->proto == IPPROTO_TCP && conn->state == DPVS_TCP_S_ESTABLISHED) - || conn->proto == IPPROTO_UDP) { + || conn->proto == IPPROTO_UDP + || (conn->proto == IPPROTO_SCTP && + conn->state == DPVS_SCTP_S_ESTABLISHED)) { conn_timeout = dp_vs_conn_get_timeout(conn); if (conn_timeout > 0) { @@ -669,7 +672,7 @@ static int dp_vs_conn_expire(void *priv) /* refcnt == 1 means we are the only referer. * no one is using the conn and it's timed out. */ - if (rte_atomic32_read(&conn->refcnt) == 1) { + if (rte_atomic32_sub_return(&conn->refcnt, 1) == 0) { dp_vs_conn_detach_timer(conn, false); /* I was controlled by someone */ @@ -684,8 +687,6 @@ static int dp_vs_conn_expire(void *priv) dp_vs_laddr_unbind(conn); dp_vs_conn_free_packets(conn); - rte_atomic32_dec(&conn->refcnt); - #ifdef CONFIG_DPVS_IPVS_STATS_DEBUG conn_stats_dump("del conn", conn); #endif @@ -696,16 +697,15 @@ static int dp_vs_conn_expire(void *priv) dp_vs_conn_free(conn); return DTIMER_STOP; - } - - dp_vs_conn_hash(conn); + } else { + dp_vs_conn_hash(conn); - /* some one is using it when expire, - * try del it again later */ - dp_vs_conn_refresh_timer(conn, false); + /* some one is using it when expire, + * try del it again later */ + dp_vs_conn_refresh_timer(conn, false); - rte_atomic32_dec(&conn->refcnt); - return DTIMER_OK; + return DTIMER_OK; + } } void dp_vs_conn_expire_now(struct dp_vs_conn *conn) @@ -1192,11 +1192,15 @@ static void dp_vs_conn_put_nolock(struct dp_vs_conn *conn) static int conn_init_lcore(void *arg) { int i; + lcoreid_t cid = rte_lcore_id(); - if (!rte_lcore_is_enabled(rte_lcore_id())) + if (cid >= DPVS_MAX_LCORE) + return EDPVS_IDLE; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; - if (!netif_lcore_is_fwd_worker(rte_lcore_id())) + if (!netif_lcore_is_fwd_worker(cid)) return EDPVS_IDLE; this_conn_tbl = rte_malloc(NULL, @@ -1218,12 +1222,16 @@ static int conn_init_lcore(void *arg) static int conn_term_lcore(void *arg) { - if (!rte_lcore_is_enabled(rte_lcore_id())) - return EDPVS_DISABLED; + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_IDLE; - conn_flush(); + if (!rte_lcore_is_enabled(cid)) + return EDPVS_DISABLED; if (this_conn_tbl) { + conn_flush(); rte_free(this_conn_tbl); this_conn_tbl = NULL; } @@ -1308,6 +1316,39 @@ static inline char* get_conn_state_name(uint16_t proto, uint16_t state) break; } break; + case IPPROTO_SCTP: + switch (state) { + case DPVS_SCTP_S_NONE: + return "SCTP_NONE"; + case DPVS_SCTP_S_INIT1: + return "SCTP_INIT1"; + case DPVS_SCTP_S_INIT: + return "SCTP_INIT"; + case DPVS_SCTP_S_COOKIE_SENT: + return "SCTP_COOKIE_SENT"; + case DPVS_SCTP_S_COOKIE_REPLIED: + return "SCTP_COOKIE_REPLIED"; + case DPVS_SCTP_S_COOKIE_WAIT: + return "SCTP_COOKIE_WAIT"; + case DPVS_SCTP_S_COOKIE: + return "SCTP_COOKIE"; + case DPVS_SCTP_S_COOKIE_ECHOED: + return "SCTP_COOKIE_ECHOED"; + case DPVS_SCTP_S_ESTABLISHED: + return "SCTP_ESTABLISHED"; + case DPVS_SCTP_S_SHUTDOWN_SENT: + return "SCTP_SHUTDOWN_SENT"; + case DPVS_SCTP_S_SHUTDOWN_RECEIVED: + return "SCTP_SHUTDOWN_RECEIVED"; + case DPVS_SCTP_S_SHUTDOWN_ACK_SENT: + return "SCTP_SHUTDOWN_ACK_SENT"; + case DPVS_SCTP_S_REJECTED: + return "SCTP_REJECTED"; + case DPVS_SCTP_S_CLOSED: + return "SCTP_CLOSED"; + default: + return "SCTP_UNKNOWN"; + } case IPPROTO_ICMP: case IPPROTO_ICMPV6: switch (state) { diff --git a/src/ipvs/ip_vs_core.c b/src/ipvs/ip_vs_core.c index b92e98496..f01f41ba0 100644 --- a/src/ipvs/ip_vs_core.c +++ b/src/ipvs/ip_vs_core.c @@ -984,9 +984,8 @@ static int __dp_vs_in(void *priv, struct rte_mbuf *mbuf, /* packet belongs to existing connection ? */ conn = prot->conn_lookup(prot, &iph, mbuf, &dir, false, &drop, &peer_cid); - if (unlikely(drop)) { - RTE_LOG(DEBUG, IPVS, "%s: deny ip try to visit.\n", __func__); + RTE_LOG(DEBUG, IPVS, "%s: packet dropped by ipvs acl\n", __func__); return INET_DROP; } diff --git a/src/ipvs/ip_vs_dest.c b/src/ipvs/ip_vs_dest.c index 8564c6ebe..4f44b0428 100644 --- a/src/ipvs/ip_vs_dest.c +++ b/src/ipvs/ip_vs_dest.c @@ -74,6 +74,10 @@ static void __dp_vs_dest_update(struct dp_vs_service *svc, int conn_flags; rte_atomic16_set(&dest->weight, udest->weight); + if (udest->flags & DPVS_DEST_F_INHIBITED) + dp_vs_dest_set_inhibited(dest); + else + dp_vs_dest_clear_inhibited(dest); conn_flags = udest->conn_flags | DPVS_CONN_F_INACTIVE; dest->fwdmode = udest->fwdmode; rte_atomic16_set(&dest->conn_flags, conn_flags); @@ -394,7 +398,7 @@ static void dest_inhibit_logging(const struct dp_vs_dest *dest, const char *msg) RTE_LOG(INFO, SERVICE, "[cid %02d, %s, svc %s:%d, rs %s:%d, weight %d, inhibited %s," " down_notice_recvd %d, inhibit_duration %ds, origin_weight %d] %s\n", cid, - dest->proto == IPPROTO_TCP ? "tcp" : "udp", + dest->proto == IPPROTO_TCP ? "tcp" : IPPROTO_UDP ? "udp" : "sctp", inet_ntop(dest->svc->af, &dest->svc->addr, str_vaddr, sizeof(str_vaddr)) ? str_vaddr : "::", ntohs(dest->svc->port), inet_ntop(dest->af, &dest->addr, str_daddr, sizeof(str_daddr)) ? str_daddr : "::", @@ -409,7 +413,7 @@ static void dest_inhibit_logging(const struct dp_vs_dest *dest, const char *msg) } else { RTE_LOG(DEBUG, SERVICE, "[cid %02d, %s, svc %s:%d, rs %s:%d, weight %d, inhibited %s, warm_up_count %d] %s\n", cid, - dest->proto == IPPROTO_TCP ? "tcp" : "udp", + dest->proto == IPPROTO_TCP ? "tcp" : IPPROTO_UDP ? "udp" : "sctp", inet_ntop(dest->svc->af, &dest->svc->addr, str_vaddr, sizeof(str_vaddr)) ? str_vaddr : "::", ntohs(dest->svc->port), inet_ntop(dest->af, &dest->addr, str_daddr, sizeof(str_daddr)) ? str_daddr : "::", diff --git a/src/ipvs/ip_vs_laddr.c b/src/ipvs/ip_vs_laddr.c index 4f15005a9..f65978d64 100644 --- a/src/ipvs/ip_vs_laddr.c +++ b/src/ipvs/ip_vs_laddr.c @@ -118,7 +118,7 @@ static inline int __laddr_step(struct dp_vs_service *svc) * */ if (strncmp(svc->scheduler->name, "rr", 2) == 0 || strncmp(svc->scheduler->name, "wrr", 3) == 0) - return (random() % 100) < 5 ? 2 : 1; + return rte_rand_max(100) < 5 ? 2 : 1; return 1; } @@ -175,7 +175,8 @@ int dp_vs_laddr_bind(struct dp_vs_conn *conn, struct dp_vs_service *svc) if (!conn || !conn->dest || !svc) return EDPVS_INVAL; - if (svc->proto != IPPROTO_TCP && svc->proto != IPPROTO_UDP) + if (svc->proto != IPPROTO_TCP && svc->proto != IPPROTO_UDP && + svc->proto != IPPROTO_SCTP) return EDPVS_NOTSUPP; if (dp_vs_conn_is_template(conn)) return EDPVS_OK; diff --git a/src/ipvs/ip_vs_nat64.c b/src/ipvs/ip_vs_nat64.c index e9a827b60..b0ff237a6 100644 --- a/src/ipvs/ip_vs_nat64.c +++ b/src/ipvs/ip_vs_nat64.c @@ -34,6 +34,7 @@ int mbuf_6to4(struct rte_mbuf *mbuf, */ if (ip6h->ip6_nxt != IPPROTO_TCP && ip6h->ip6_nxt != IPPROTO_UDP && + ip6h->ip6_nxt != IPPROTO_SCTP && ip6h->ip6_nxt != IPPROTO_ICMPV6 && ip6h->ip6_nxt != IPPROTO_OPT) { return EDPVS_NOTSUPP; diff --git a/src/ipvs/ip_vs_proto.c b/src/ipvs/ip_vs_proto.c index bb5baa5ff..9d7f1e422 100644 --- a/src/ipvs/ip_vs_proto.c +++ b/src/ipvs/ip_vs_proto.c @@ -71,6 +71,7 @@ struct dp_vs_proto *dp_vs_proto_lookup(uint8_t proto) extern struct dp_vs_proto dp_vs_proto_udp; extern struct dp_vs_proto dp_vs_proto_tcp; +extern struct dp_vs_proto dp_vs_proto_sctp; extern struct dp_vs_proto dp_vs_proto_icmp; extern struct dp_vs_proto dp_vs_proto_icmp6; @@ -88,6 +89,11 @@ int dp_vs_proto_init(void) goto tcp_error; } + if ((err = proto_register(&dp_vs_proto_sctp)) != EDPVS_OK) { + RTE_LOG(ERR, IPVS, "%s: fail to register SCTP\n", __func__); + goto sctp_error; + } + if ((err = proto_register(&dp_vs_proto_icmp6)) != EDPVS_OK) { RTE_LOG(ERR, IPVS, "%s: fail to register ICMPV6\n", __func__); goto icmp6_error; @@ -103,6 +109,8 @@ int dp_vs_proto_init(void) icmp_error: proto_unregister(&dp_vs_proto_icmp6); icmp6_error: + proto_unregister(&dp_vs_proto_sctp); +sctp_error: proto_unregister(&dp_vs_proto_tcp); tcp_error: proto_unregister(&dp_vs_proto_udp); @@ -117,6 +125,9 @@ int dp_vs_proto_term(void) if (proto_unregister(&dp_vs_proto_icmp6) != EDPVS_OK) RTE_LOG(ERR, IPVS, "%s: fail to unregister ICMPV6\n", __func__); + if (proto_unregister(&dp_vs_proto_sctp) != EDPVS_OK) + RTE_LOG(ERR, IPVS, "%s: fail to unregister SCTP\n", __func__); + if (proto_unregister(&dp_vs_proto_tcp) != EDPVS_OK) RTE_LOG(ERR, IPVS, "%s: fail to unregister TCP\n", __func__); diff --git a/src/ipvs/ip_vs_proto_sctp.c b/src/ipvs/ip_vs_proto_sctp.c new file mode 100644 index 000000000..be2330e5f --- /dev/null +++ b/src/ipvs/ip_vs_proto_sctp.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "conf/common.h" +#include "dpdk.h" +#include "mbuf.h" +#include "ipv6.h" +#include "route6.h" +#include "neigh.h" +#include "ipvs/ipvs.h" +#include "ipvs/proto.h" +#include "ipvs/proto_sctp.h" +#include "ipvs/conn.h" +#include "ipvs/service.h" +#include "ipvs/dest.h" +#include "ipvs/synproxy.h" +#include "ipvs/blklst.h" +#include "ipvs/whtlst.h" +#include "parser/parser.h" +#include "rte_hash_crc.h" + +/* + * Compute the SCTP checksum in network byte order for a given mbuf chain m + * which contains an SCTP packet starting at offset. + * Since this function is also called by ipfw, don't assume that + * it is compiled on a kernel with SCTP support. + */ +static inline uint32_t sctp_calculate_cksum(struct rte_mbuf *mbuf, + int32_t offset) +{ + int len; + uint32_t _old, _new; + + len = mbuf->data_len; + + struct sctphdr *sh = + rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, offset); + + _old = sh->checksum; + + sh->checksum = 0; + + _new = ~rte_hash_crc(rte_pktmbuf_mtod_offset(mbuf, const void *, + offset), + len - offset, ~(uint32_t)0); + + sh->checksum = _old; + + return _new; +} + +static int sctp_csum_check(struct dp_vs_proto *proto, int af, + struct rte_mbuf *mbuf); + +static struct dp_vs_conn *sctp_conn_lookup(struct dp_vs_proto *proto, + const struct dp_vs_iphdr *iph, + struct rte_mbuf *mbuf, int *direct, + bool reverse, bool *drop, + lcoreid_t *peer_cid) +{ + struct sctphdr *sh, _sctph; + struct sctp_chunkhdr *sch, _schunkh; + struct dp_vs_conn *conn; + assert(proto && iph && mbuf); + + sh = mbuf_header_pointer(mbuf, iph->len, sizeof(_sctph), &_sctph); + if (unlikely(!sh)) + return NULL; + + sch = mbuf_header_pointer(mbuf, iph->len + sizeof(_sctph), + sizeof(_schunkh), &_schunkh); + if (unlikely(!sch)) + return NULL; + + if (dp_vs_blklst_filtered(iph->af, iph->proto, &iph->daddr, sh->dest_port, + &iph->saddr, mbuf)) { + *drop = true; + return NULL; + } + + if (dp_vs_whtlst_filtered(iph->af, iph->proto, &iph->daddr, sh->dest_port, + &iph->saddr, mbuf)) { + *drop = true; + return NULL; + } + + conn = dp_vs_conn_get(iph->af, iph->proto, &iph->saddr, &iph->daddr, + sh->src_port, sh->dest_port, direct, reverse); + + /* + * L2 confirm neighbour + * pkt in from client confirm neighbour to client + * pkt out from rs confirm neighbour to rs + */ + if (conn != NULL) { + if ((*direct == DPVS_CONN_DIR_INBOUND) && conn->out_dev && + (!inet_is_addr_any(tuplehash_in(conn).af, + &conn->out_nexthop))) { + neigh_confirm(tuplehash_in(conn).af, &conn->out_nexthop, + conn->out_dev); + } else if ((*direct == DPVS_CONN_DIR_OUTBOUND) && + conn->in_dev && + (!inet_is_addr_any(tuplehash_out(conn).af, + &conn->in_nexthop))) { + neigh_confirm(tuplehash_out(conn).af, &conn->in_nexthop, + conn->in_dev); + } + } else { + struct dp_vs_redirect *r; + + r = dp_vs_redirect_get(iph->af, iph->proto, &iph->saddr, + &iph->daddr, sh->src_port, + sh->dest_port); + if (r) { + *peer_cid = r->cid; + } + } + + return conn; +} + +static int sctp_conn_schedule(struct dp_vs_proto *proto, + const struct dp_vs_iphdr *iph, + struct rte_mbuf *mbuf, struct dp_vs_conn **conn, + int *verdict) +{ + struct sctphdr *sh, _sctph; + struct dp_vs_service *svc; + + assert(proto && iph && mbuf && conn && verdict); + + sh = mbuf_header_pointer(mbuf, iph->len, sizeof(_sctph), &_sctph); + if (unlikely(!sh)) { + *verdict = INET_DROP; + return EDPVS_INVPKT; + } + + svc = dp_vs_service_lookup(iph->af, iph->proto, &iph->daddr, + sh->dest_port, 0, mbuf, NULL, rte_lcore_id()); + if (!svc) { + *verdict = INET_ACCEPT; + return EDPVS_NOSERV; + } + + *conn = dp_vs_schedule(svc, iph, mbuf, false); + if (!*conn) { + *verdict = INET_DROP; + return EDPVS_RESOURCE; + } + + return EDPVS_OK; +} + +static void sctp_nat_csum(struct rte_mbuf *mbuf, struct sctphdr *sctph, + unsigned int offset) +{ + sctph->checksum = sctp_calculate_cksum(mbuf, offset); +} + +static int sctp_fnat_in_handler(struct dp_vs_proto *proto, + struct dp_vs_conn *conn, struct rte_mbuf *mbuf) +{ + struct sctphdr *sh; + + /* af/mbuf may be changed for nat64 which in af is ipv6 and out is ipv4 */ + int af = tuplehash_out(conn).af; + int iphdrlen = ((AF_INET6 == af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + if (mbuf_may_pull(mbuf, iphdrlen + sizeof(*sh)) != 0) + return EDPVS_INVPKT; + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, iphdrlen); + + /* Some checks before mangling */ + if (sctp_csum_check(proto, af, mbuf)) + return EDPVS_INVAL; + + /* L4 translation */ + sh->src_port = conn->lport; + sh->dest_port = conn->dport; + + sctp_nat_csum(mbuf, sh, iphdrlen); + + return EDPVS_OK; +} + +static int sctp_fnat_out_handler(struct dp_vs_proto *proto, + struct dp_vs_conn *conn, struct rte_mbuf *mbuf) +{ + struct sctphdr *sh; + + /* af/mbuf may be changed for nat64 which in af is ipv6 and out is ipv4 */ + int af = tuplehash_in(conn).af; + int iphdrlen = ((AF_INET6 == af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + if (mbuf_may_pull(mbuf, iphdrlen + sizeof(*sh)) != 0) + return EDPVS_INVPKT; + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, iphdrlen); + + /* Some checks before mangling */ + if (sctp_csum_check(proto, af, mbuf)) + return EDPVS_INVAL; + + /* L4 translation */ + sh->src_port = conn->vport; + sh->dest_port = conn->cport; + + sctp_nat_csum(mbuf, sh, iphdrlen); + + return EDPVS_OK; +} + +static int sctp_nat_in_handler(struct dp_vs_proto *proto, + struct dp_vs_conn *conn, struct rte_mbuf *mbuf) +{ + struct sctphdr *sh; + int af = conn->af; + int iphdrlen = ((AF_INET6 == af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + if (mbuf_may_pull(mbuf, iphdrlen + sizeof(*sh)) != 0) + return EDPVS_INVPKT; + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, iphdrlen); + + /* Some checks before mangling */ + if (sctp_csum_check(proto, af, mbuf)) + return EDPVS_INVAL; + + /* Only update csum if we really have to */ + sh->dest_port = conn->dport; + sctp_nat_csum(mbuf, sh, iphdrlen); + + return EDPVS_OK; +} + +static int sctp_nat_out_handler(struct dp_vs_proto *proto, + struct dp_vs_conn *conn, struct rte_mbuf *mbuf) +{ + struct sctphdr *sh; + int af = conn->af; + int iphdrlen = ((AF_INET6 == af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + if (mbuf_may_pull(mbuf, iphdrlen + sizeof(*sh)) != 0) + return EDPVS_INVPKT; + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, iphdrlen); + + /* Some checks before mangling */ + if (sctp_csum_check(proto, af, mbuf)) + return EDPVS_INVAL; + + /* Only update csum if we really have to */ + sh->src_port = conn->vport; + sctp_nat_csum(mbuf, sh, iphdrlen); + + return EDPVS_OK; +} + +static int sctp_csum_check(struct dp_vs_proto *proto, int af, + struct rte_mbuf *mbuf) +{ + struct sctphdr *sh; + uint32_t cmp, val; + int iphdrlen = ((AF_INET6 == af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, iphdrlen); + cmp = sh->checksum; + val = sctp_calculate_cksum(mbuf, iphdrlen); + + if (val != cmp) { + /* CRC failure, dump it. */ + RTE_LOG(WARNING, IPVS, "Failed checksum for %d %s %p!\n", af, + proto->name, mbuf); + return EDPVS_INVAL; + } + return EDPVS_OK; +} + +/* RFC 2960, 3.2 Chunk Field Descriptions */ +static __u8 sctp_events[] = { + [SCTP_DATA] = DPVS_SCTP_DATA, + [SCTP_INITIATION] = DPVS_SCTP_INIT, + [SCTP_INITIATION_ACK] = DPVS_SCTP_INIT_ACK, + [SCTP_SELECTIVE_ACK] = DPVS_SCTP_DATA, + [SCTP_HEARTBEAT_REQUEST] = DPVS_SCTP_DATA, + [SCTP_HEARTBEAT_ACK] = DPVS_SCTP_DATA, + [SCTP_ABORT_ASSOCIATION] = DPVS_SCTP_ABORT, + [SCTP_SHUTDOWN] = DPVS_SCTP_SHUTDOWN, + [SCTP_SHUTDOWN_ACK] = DPVS_SCTP_SHUTDOWN_ACK, + [SCTP_OPERATION_ERROR] = DPVS_SCTP_ERROR, + [SCTP_COOKIE_ECHO] = DPVS_SCTP_COOKIE_ECHO, + [SCTP_COOKIE_ACK] = DPVS_SCTP_COOKIE_ACK, + [SCTP_ECN_ECHO] = DPVS_SCTP_DATA, + [SCTP_ECN_CWR] = DPVS_SCTP_DATA, + [SCTP_SHUTDOWN_COMPLETE] = DPVS_SCTP_SHUTDOWN_COMPLETE, +}; + +/* SCTP States: + * See RFC 2960, 4. SCTP Association State Diagram + * + * New states (not in diagram): + * - INIT1 state: use shorter timeout for dropped INIT packets + * - REJECTED state: use shorter timeout if INIT is rejected with ABORT + * - INIT, COOKIE_SENT, COOKIE_REPLIED, COOKIE states: for better debugging + * + * The states are as seen in real server. In the diagram, INIT1, INIT, + * COOKIE_SENT and COOKIE_REPLIED processing happens in CLOSED state. + * + * States as per packets from client (C) and server (S): + * + * Setup of client connection: + * DPVS_SCTP_S_INIT1: First C:INIT sent, wait for S:INIT-ACK + * DPVS_SCTP_S_INIT: Next C:INIT sent, wait for S:INIT-ACK + * DPVS_SCTP_S_COOKIE_SENT: S:INIT-ACK sent, wait for C:COOKIE-ECHO + * DPVS_SCTP_S_COOKIE_REPLIED: C:COOKIE-ECHO sent, wait for S:COOKIE-ACK + * + * Setup of server connection: + * DPVS_SCTP_S_COOKIE_WAIT: S:INIT sent, wait for C:INIT-ACK + * DPVS_SCTP_S_COOKIE: C:INIT-ACK sent, wait for S:COOKIE-ECHO + * DPVS_SCTP_S_COOKIE_ECHOED: S:COOKIE-ECHO sent, wait for C:COOKIE-ACK + */ + +#define sNO DPVS_SCTP_S_NONE +#define sI1 DPVS_SCTP_S_INIT1 +#define sIN DPVS_SCTP_S_INIT +#define sCS DPVS_SCTP_S_COOKIE_SENT +#define sCR DPVS_SCTP_S_COOKIE_REPLIED +#define sCW DPVS_SCTP_S_COOKIE_WAIT +#define sCO DPVS_SCTP_S_COOKIE +#define sCE DPVS_SCTP_S_COOKIE_ECHOED +#define sES DPVS_SCTP_S_ESTABLISHED +#define sSS DPVS_SCTP_S_SHUTDOWN_SENT +#define sSR DPVS_SCTP_S_SHUTDOWN_RECEIVED +#define sSA DPVS_SCTP_S_SHUTDOWN_ACK_SENT +#define sRJ DPVS_SCTP_S_REJECTED +#define sCL DPVS_SCTP_S_CLOSED + +static const __u8 sctp_states[DPVS_DIR_LAST][DPVS_SCTP_EVENT_LAST][DPVS_SCTP_S_LAST] = { + { /* INPUT */ +/* sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/ +/* d */ { sES, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* i */ { sI1, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sIN, sIN }, +/* i_a */ { sCW, sCW, sCW, sCS, sCR, sCO, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_e */ { sCR, sIN, sIN, sCR, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_a */ { sES, sI1, sIN, sCS, sCR, sCW, sCO, sES, sES, sSS, sSR, sSA, sRJ, sCL }, +/* s */ { sSR, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sSR, sSS, sSR, sSA, sRJ, sCL }, +/* s_a */ { sCL, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sES, sCL, sSR, sCL, sRJ, sCL }, +/* s_c */ { sCL, sCL, sCL, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sCL, sRJ, sCL }, +/* err */ { sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCL, sES, sSS, sSR, sSA, sRJ, sCL }, +/* ab */ { sCL, sCL, sCL, sCL, sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL }, + }, + { /* OUTPUT */ +/* sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/ +/* d */ { sES, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* i */ { sCW, sCW, sCW, sCW, sCW, sCW, sCW, sCW, sES, sCW, sCW, sCW, sCW, sCW }, +/* i_a */ { sCS, sCS, sCS, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_e */ { sCE, sCE, sCE, sCE, sCE, sCE, sCE, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_a */ { sES, sES, sES, sES, sES, sES, sES, sES, sES, sSS, sSR, sSA, sRJ, sCL }, +/* s */ { sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSR, sSA, sRJ, sCL }, +/* s_a */ { sSA, sSA, sSA, sSA, sSA, sCW, sCO, sCE, sES, sSA, sSA, sSA, sRJ, sCL }, +/* s_c */ { sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* err */ { sCL, sCL, sCL, sCL, sCL, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* ab */ { sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL }, + }, + { /* INPUT-ONLY */ +/* sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/ +/* d */ { sES, sI1, sIN, sCS, sCR, sES, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* i */ { sI1, sIN, sIN, sIN, sIN, sIN, sCO, sCE, sES, sSS, sSR, sSA, sIN, sIN }, +/* i_a */ { sCE, sCE, sCE, sCE, sCE, sCE, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_e */ { sES, sES, sES, sES, sES, sES, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* c_a */ { sES, sI1, sIN, sES, sES, sCW, sES, sES, sES, sSS, sSR, sSA, sRJ, sCL }, +/* s */ { sSR, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sSR, sSS, sSR, sSA, sRJ, sCL }, +/* s_a */ { sCL, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sCL, sCL, sSR, sCL, sRJ, sCL }, +/* s_c */ { sCL, sCL, sCL, sCL, sCL, sCW, sCO, sCE, sES, sSS, sCL, sCL, sRJ, sCL }, +/* err */ { sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL }, +/* ab */ { sCL, sCL, sCL, sCL, sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL }, + }, +}; + +#define DPVS_SCTP_MAX_RTO (60 + 1) + +/* Timeout table[state] */ +static int sctp_timeouts[DPVS_SCTP_S_LAST + 1] = { + [DPVS_SCTP_S_NONE] = 2, + [DPVS_SCTP_S_INIT1] = (0 + 3 + 1), + [DPVS_SCTP_S_INIT] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_COOKIE_SENT] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_COOKIE_REPLIED] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_COOKIE_WAIT] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_COOKIE] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_COOKIE_ECHOED] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_ESTABLISHED] = 15 * 60, + [DPVS_SCTP_S_SHUTDOWN_SENT] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_SHUTDOWN_RECEIVED] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_SHUTDOWN_ACK_SENT] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_REJECTED] = (0 + 3 + 1), + [DPVS_SCTP_S_CLOSED] = DPVS_SCTP_MAX_RTO, + [DPVS_SCTP_S_LAST] = 2, +}; + +static const char *sctp_state_name_table[DPVS_SCTP_S_LAST + 1] = { + [DPVS_SCTP_S_NONE] = "NONE", + [DPVS_SCTP_S_INIT1] = "INIT1", + [DPVS_SCTP_S_INIT] = "INIT", + [DPVS_SCTP_S_COOKIE_SENT] = "C-SENT", + [DPVS_SCTP_S_COOKIE_REPLIED] = "C-REPLIED", + [DPVS_SCTP_S_COOKIE_WAIT] = "C-WAIT", + [DPVS_SCTP_S_COOKIE] = "COOKIE", + [DPVS_SCTP_S_COOKIE_ECHOED] = "C-ECHOED", + [DPVS_SCTP_S_ESTABLISHED] = "ESTABLISHED", + [DPVS_SCTP_S_SHUTDOWN_SENT] = "S-SENT", + [DPVS_SCTP_S_SHUTDOWN_RECEIVED] = "S-RECEIVED", + [DPVS_SCTP_S_SHUTDOWN_ACK_SENT] = "S-ACK-SENT", + [DPVS_SCTP_S_REJECTED] = "REJECTED", + [DPVS_SCTP_S_CLOSED] = "CLOSED", + [DPVS_SCTP_S_LAST] = "BUG!", +}; + +static const char *sctp_state_name(int state) +{ + if (state >= DPVS_SCTP_S_LAST) + return "ERR!"; + if (sctp_state_name_table[state]) + return sctp_state_name_table[state]; + return "?"; +} + +static int sctp_state_trans(struct dp_vs_proto *proto, struct dp_vs_conn *conn, + struct rte_mbuf *mbuf, int dir) +{ + struct sctp_chunkhdr _sctpch, *sch; + unsigned char chunk_type; + int event, next_state; + int iphdrlen, cofs; + assert(proto && conn && mbuf); + + iphdrlen = + ((AF_INET6 == conn->af) ? ip6_hdrlen(mbuf) : ip4_hdrlen(mbuf)); + + cofs = iphdrlen + sizeof(struct sctphdr); + sch = mbuf_header_pointer(mbuf, cofs, sizeof(_sctpch), &_sctpch); + if (!sch) + return EDPVS_INVPKT; + + chunk_type = sch->chunk_type; + /* + * Section 3: Multiple chunks can be bundled into one SCTP packet + * up to the MTU size, except for the INIT, INIT ACK, and + * SHUTDOWN COMPLETE chunks. These chunks MUST NOT be bundled with + * any other chunk in a packet. + * + * Section 3.3.7: DATA chunks MUST NOT be bundled with ABORT. Control + * chunks (except for INIT, INIT ACK, and SHUTDOWN COMPLETE) MAY be + * bundled with an ABORT, but they MUST be placed before the ABORT + * in the SCTP packet or they will be ignored by the receiver. + */ + if ((sch->chunk_type == SCTP_COOKIE_ECHO) || + (sch->chunk_type == SCTP_COOKIE_ACK)) { + int clen = ntohs(sch->chunk_length); + + if (clen >= sizeof(_sctpch)) { + sch = mbuf_header_pointer(mbuf, + cofs + RTE_ALIGN(clen, 4), + sizeof(_sctpch), &_sctpch); + if (sch && sch->chunk_type == SCTP_ABORT_ASSOCIATION) + chunk_type = sch->chunk_type; + } + } + + event = (chunk_type < sizeof(sctp_events)) ? sctp_events[chunk_type] : + DPVS_SCTP_DATA; + + next_state = sctp_states[dir][event][conn->state]; + + if (next_state != conn->state) { + struct dp_vs_dest *dest = conn->dest; + +#ifdef CONFIG_DPVS_IPVS_DEBUG + RTE_LOG(DEBUG, IPVS, + "%s %s %X:%d->" + "%X:%d state: %s->%s conn->refcnt:%d\n", + proto->name, + ((dir == DPVS_CONN_DIR_OUTBOUND) ? "output " : + "input "), + inet_addr_fold(conn->af, &conn->caddr), + ntohs(conn->dport), + inet_addr_fold(conn->af, &conn->caddr), + ntohs(conn->cport), sctp_state_name(conn->state), + sctp_state_name(next_state), + rte_atomic32_read(&conn->refcnt)); +#endif + if (dest) { + if (!(conn->flags & DPVS_CONN_F_INACTIVE) && + (next_state != DPVS_SCTP_S_ESTABLISHED)) { + rte_atomic32_dec(&dest->actconns); + rte_atomic32_inc(&dest->inactconns); + conn->flags |= DPVS_CONN_F_INACTIVE; + } else if ((conn->flags & DPVS_CONN_F_INACTIVE) && + (next_state == DPVS_SCTP_S_ESTABLISHED)) { + rte_atomic32_inc(&dest->actconns); + rte_atomic32_dec(&dest->inactconns); + conn->flags &= ~DPVS_CONN_F_INACTIVE; + } + } + conn->old_state = conn->state; + conn->state = next_state; + } + dp_vs_conn_set_timeout(conn, proto); + return EDPVS_OK; +} + +static int sctp_conn_expire(struct dp_vs_proto *proto, struct dp_vs_conn *conn) +{ + if (conn && conn->prot_data) + rte_free(conn->prot_data); + + return EDPVS_OK; +} + +static int sctp_conn_expire_quiescent(struct dp_vs_conn *conn) +{ + dp_vs_conn_expire_now(conn); + + return EDPVS_OK; +} + +static int sctp_init(struct dp_vs_proto *proto) +{ + if (!proto) + return EDPVS_INVAL; + + proto->timeout_table = sctp_timeouts; + + return EDPVS_OK; +} + +static int sctp_exit(struct dp_vs_proto *proto) +{ + return EDPVS_OK; +} + +struct dp_vs_proto dp_vs_proto_sctp = { + .name = "SCTP", + .proto = IPPROTO_SCTP, + .init = sctp_init, + .exit = sctp_exit, + .conn_sched = sctp_conn_schedule, + .conn_lookup = sctp_conn_lookup, + .conn_expire = sctp_conn_expire, + .conn_expire_quiescent = sctp_conn_expire_quiescent, + .nat_in_handler = sctp_nat_in_handler, + .nat_out_handler = sctp_nat_out_handler, + .fnat_in_handler = sctp_fnat_in_handler, + .fnat_out_handler = sctp_fnat_out_handler, + .snat_in_handler = sctp_nat_in_handler, + .snat_out_handler = sctp_nat_out_handler, + .state_trans = sctp_state_trans, + .state_name = sctp_state_name, +}; diff --git a/src/ipvs/ip_vs_proto_tcp.c b/src/ipvs/ip_vs_proto_tcp.c index 8c7edb501..b10aedd49 100644 --- a/src/ipvs/ip_vs_proto_tcp.c +++ b/src/ipvs/ip_vs_proto_tcp.c @@ -831,13 +831,14 @@ tcp_conn_lookup(struct dp_vs_proto *proto, const struct dp_vs_iphdr *iph, if (unlikely(!th)) return NULL; - if (dp_vs_blklst_lookup(iph->af, iph->proto, &iph->daddr, - th->dest, &iph->saddr)) { + if (dp_vs_blklst_filtered(iph->af, iph->proto, &iph->daddr, + th->dest, &iph->saddr, mbuf)) { *drop = true; return NULL; } - if (!dp_vs_whtlst_allow(iph->af, iph->proto, &iph->daddr, th->dest, &iph->saddr)) { + if (dp_vs_whtlst_filtered(iph->af, iph->proto, &iph->daddr, + th->dest, &iph->saddr, mbuf)) { *drop = true; return NULL; } diff --git a/src/ipvs/ip_vs_proto_udp.c b/src/ipvs/ip_vs_proto_udp.c index a3f2a4690..b0fa040f2 100644 --- a/src/ipvs/ip_vs_proto_udp.c +++ b/src/ipvs/ip_vs_proto_udp.c @@ -26,6 +26,7 @@ #include "ipvs/ipvs.h" #include "ipvs/proto.h" #include "ipvs/proto_udp.h" +#include "ipvs/quic.h" #include "ipvs/conn.h" #include "ipvs/service.h" #include "ipvs/blklst.h" @@ -174,10 +175,32 @@ static int udp_conn_sched(struct dp_vs_proto *proto, } /* schedule RS and create new connection */ - *conn = dp_vs_schedule(svc, iph, mbuf, false); + *conn = NULL; + if (svc->flags & DP_VS_SVC_F_QUIC) { // deal with quic conn migration + struct quic_server qsvr = { 0 }; + int err = quic_parse_server(mbuf, iph, &qsvr); + if (likely(err == EDPVS_OK)) { + if (qsvr.wildcard > 0) { + *conn = quic_schedule(svc, &qsvr, iph, mbuf); + if (*conn) + RTE_LOG(INFO, IPVS, "schedule new connection from quic cid\n"); + else { + // Do NOT emit warning log here! + // The DCID in Initial packets are generated randomly by client, which + // doesn't contain valid server address info for success schedule. + } + } + } else { + RTE_LOG(WARNING, IPVS, "fail to parse server info from quic mbuf: %s\n", + dpvs_strerror(err)); + } + } if (!*conn) { - *verdict = INET_DROP; - return EDPVS_RESOURCE; + *conn = dp_vs_schedule(svc, iph, mbuf, false); + if (!*conn) { + *verdict = INET_DROP; + return EDPVS_RESOURCE; + } } if ((*conn)->dest->fwdmode == DPVS_FWD_MODE_FNAT && g_uoa_max_trail > 0) { @@ -212,14 +235,14 @@ udp_conn_lookup(struct dp_vs_proto *proto, if (unlikely(!uh)) return NULL; - if (dp_vs_blklst_lookup(iph->af, iph->proto, &iph->daddr, - uh->dst_port, &iph->saddr)) { + if (dp_vs_blklst_filtered(iph->af, iph->proto, &iph->daddr, + uh->dst_port, &iph->saddr, mbuf)) { *drop = true; return NULL; } - if (!dp_vs_whtlst_allow(iph->af, iph->proto, &iph->daddr, - uh->dst_port, &iph->saddr)) { + if (dp_vs_whtlst_filtered(iph->af, iph->proto, &iph->daddr, + uh->dst_port, &iph->saddr, mbuf)) { *drop = true; return NULL; } diff --git a/src/ipvs/ip_vs_proxy_proto.c b/src/ipvs/ip_vs_proxy_proto.c index 8f08f9228..9daf7f2c8 100644 --- a/src/ipvs/ip_vs_proxy_proto.c +++ b/src/ipvs/ip_vs_proxy_proto.c @@ -423,7 +423,7 @@ int proxy_proto_insert(struct proxy_info *ppinfo, struct dp_vs_conn *conn, void *rt; int ppdoff, ppdatalen, room, mtu; int oaf; - char ppv1buf[108], tbuf1[64], tbuf2[64]; + char ppv1buf[108], tbuf1[INET6_ADDRSTRLEN], tbuf2[INET6_ADDRSTRLEN]; struct proxy_hdr_v2 *pphv2; assert(ppinfo && conn && mbuf && l4hdr); @@ -484,8 +484,10 @@ int proxy_proto_insert(struct proxy_info *ppinfo, struct dp_vs_conn *conn, if (unlikely(NULL == inet_ntop(AF_INET, &ppinfo->addr.ip4.dst_addr, tbuf2, sizeof(tbuf2)))) return EDPVS_INVAL; - sprintf(ppv1buf, "PROXY TCP4 %s %s %d %d\r\n", tbuf1, tbuf2, - ntohs(ppinfo->addr.ip4.src_port), ntohs(ppinfo->addr.ip4.dst_port)); + if (unlikely(snprintf(ppv1buf, sizeof(ppv1buf), "PROXY TCP4 %s %s %d %d\r\n", + tbuf1, tbuf2, ntohs(ppinfo->addr.ip4.src_port), + ntohs(ppinfo->addr.ip4.dst_port)) > sizeof(ppv1buf))) + return EDPVS_INVAL; break; case AF_INET6: if (unlikely(NULL == inet_ntop(AF_INET6, ppinfo->addr.ip6.src_addr, @@ -494,8 +496,10 @@ int proxy_proto_insert(struct proxy_info *ppinfo, struct dp_vs_conn *conn, if (unlikely(NULL == inet_ntop(AF_INET6, ppinfo->addr.ip6.dst_addr, tbuf2, sizeof(tbuf2)))) return EDPVS_INVAL; - sprintf(ppv1buf, "PROXY TCP6 %s %s %d %d\r\n", tbuf1, tbuf2, - ntohs(ppinfo->addr.ip6.src_port), ntohs(ppinfo->addr.ip6.dst_port)); + if (unlikely(snprintf(ppv1buf, sizeof(ppv1buf), "PROXY TCP6 %s %s %d %d\r\n", + tbuf1, tbuf2, ntohs(ppinfo->addr.ip6.src_port), + ntohs(ppinfo->addr.ip6.dst_port)) > sizeof(ppv1buf))) + return EDPVS_INVAL; break; default: return EDPVS_NOTSUPP; diff --git a/src/ipvs/ip_vs_quic.c b/src/ipvs/ip_vs_quic.c new file mode 100644 index 000000000..69e9118ab --- /dev/null +++ b/src/ipvs/ip_vs_quic.c @@ -0,0 +1,240 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ + +#include "ipvs/quic.h" +#include "ipvs/conn.h" + +/* + * quic_parse_server extract encoded RS info from DCID in quic packet header. + * Note there exists two header types in quic. + * (https://datatracker.ietf.org/doc/html/rfc9000#name-packet-formats) + * + * Long Header Packet { + * Header Form (1) = 1, + * Fixed Bit (1) = 1, + * Long Packet Type (2), + * Type-Specific Bits (4), + * Version (32), + * Destination Connection ID Length (8), + * Destination Connection ID (0..160), + * Source Connection ID Length (8), + * Source Connection ID (0..160), + * Type-Specific Payload (..), + * } + * + * 1-RTT Packet { + * Header Form (1) = 0, + * Fixed Bit (1) = 1, + * Spin Bit (1), + * Reserved Bits (2), + * Key Phase (1), + * Packet Number Length (2), + * Destination Connection ID (0..160), + * Packet Number (8..32), + * Packet Payload (8..), + * } + */ +union quic_header { + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) || (__BYTE_ORDER == __LITTLE_ENDIAN) + unsigned int typedata:4; + unsigned int type:2; + unsigned int fixed:1; + unsigned int form:1; +#elif defined (__BIG_ENDIAN_BITFIELD) || (__BYTE_ORDER == __BIG_ENDIAN) + unsigned int form:1; + unsigned int fixed:1; + unsigned int type:2; + unsigned int typedata:4; +#else +#error "Please fix " +#endif + unsigned char extra[0]; // version, DCID len, DCID, SCID len, SCID, packet number, payload + } lhdr; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) || (__BYTE_ORDER == __LITTLE_ENDIAN) + unsigned int pkt_num_len:2; + unsigned int key_phase:1; + unsigned int reserved:2; + unsigned int spin:1; + unsigned int fixed:1; + unsigned int form:1; +#elif defined (__BIG_ENDIAN_BITFIELD) || (__BYTE_ORDER == __BIG_ENDIAN) + unsigned int form:1; + unsigned int fixed:1; + unsigned int spin:1; + unsigned int reserved:2; + unsigned int key_phase:1; + unsigned int pkt_num_len:2; +#else +#error "Please fix " +#endif + unsigned char extra[0]; // DCID, packet number, payload + } shdr; +}; + + +static inline bool quic_server_match(const struct quic_server *qsvr, + const struct dp_vs_dest *dest) { + int l3len; + const unsigned char *ptr1, *ptr2; + + if (unlikely(!qsvr->wildcard || qsvr->wildcard % 8)) + return false; + l3len = qsvr->wildcard >> 3; + + if (AF_INET == dest->af) { + ptr1 = ((const unsigned char *)&dest->addr) + (4 - l3len); + ptr2 = ((const unsigned char *)&qsvr->addr) + (4 - l3len); + } else { + ptr1 = ((const unsigned char *)&dest->addr) + (16 - l3len); + ptr2 = ((const unsigned char *)&qsvr->addr) + (16 - l3len); + } + + while (l3len-- > 0) { + if (*ptr1 != *ptr2) + return false; + ptr1++; + ptr2++; + } + + if (!qsvr->port) + return true; + return qsvr->port == dest->port; +} + +int quic_parse_server(const struct rte_mbuf *mbuf, + const struct dp_vs_iphdr *iph, + struct quic_server *qsvr) { + int offset = iph->len; + int i, l3len, l4len; + unsigned char *ptr, *dptr; + + union quic_header *qhdr, hdrbuf; + uint32_t *qver, qverbuf; + uint8_t *cidlen, cidlenbuf; + unsigned char *cid, cidbuf[20]; + + memset(qsvr, 0, sizeof(struct quic_server)); + + offset += sizeof(struct rte_udp_hdr); + qhdr = mbuf_header_pointer(mbuf, offset, sizeof(hdrbuf), &hdrbuf); + if (unlikely(!qhdr)) + return EDPVS_INVPKT; + + if (unlikely(!qhdr->lhdr.fixed)) + return EDPVS_INVPKT; + + offset++; + if (qhdr->lhdr.form) { // quic long header + qver = mbuf_header_pointer(mbuf, offset, sizeof(qverbuf), &qverbuf); + if (unlikely(NULL == qver || ntohl(*qver) > 1)) + return EDPVS_INVPKT; + offset += sizeof(qverbuf); + cidlen = mbuf_header_pointer(mbuf, offset, sizeof(cidlenbuf), &cidlenbuf); + if (unlikely(NULL == cidlen || *cidlen > 20)) + return EDPVS_INVPKT; + if (*cidlen < DPVS_QUIC_DCID_BYTES_MIN) + return EDPVS_OK; // possible conn without DCID, or cilient Initial packets + offset += sizeof(cidlenbuf); + cid = mbuf_header_pointer(mbuf, offset, *cidlen, &cidbuf); + if (unlikely(!cid)) + return EDPVS_INVPKT; + } else { // quic short header + cid = mbuf_header_pointer(mbuf, offset, DPVS_QUIC_DCID_BYTES_MIN, &cidbuf); + if (NULL == cid) + return EDPVS_OK; // possible conn without DCID + ptr = cid + 1; + cidlen = &cidlenbuf; + *cidlen = ((*ptr >> 5) & 0x3) + 1; + if (*ptr & 0x10) + *cidlen += 2; + *cidlen += 6; + if (*cidlen > DPVS_QUIC_DCID_BYTES_MIN) { + cid = mbuf_header_pointer(mbuf, offset, *cidlen, &cidbuf); + if (unlikely(!cid)) + return EDPVS_OK; // possible conn without DCID + } + } + + ptr = cid; + ++ptr; // skip first octet + l3len = ((*ptr >> 5) & 0x7) + 1; + l4len = (*ptr & 0x10) ? 2 : 0; + + qsvr->wildcard = l3len << 3; + + if (AF_INET == iph->af) + dptr = ((unsigned char *)&qsvr->addr) + (4 - l3len); + else + dptr = ((unsigned char *)&qsvr->addr) + (16 - l3len); + + for (i = 0; i < l3len; i++, ptr++, dptr++) + *dptr = ((*ptr & 0xf) << 4) | ((*(ptr+1) >> 4) & 0xf); + + if (l4len) { + dptr = (unsigned char *)&qsvr->port; + for (i = 0; i < l4len; i++, ptr++, dptr++) + *dptr = ((*ptr & 0xf) << 4) | ((*(ptr+1) >> 4) & 0xf); + } + + return EDPVS_OK; +} + +struct dp_vs_conn* quic_schedule(const struct dp_vs_service *svc, + const struct quic_server *qsvr, + const struct dp_vs_iphdr *iph, + struct rte_mbuf *mbuf) { + bool found = false; + struct dp_vs_dest *dest; + uint16_t _ports[2], *ports; + uint32_t flags = 0; + struct dp_vs_conn_param param; + struct dp_vs_conn *conn; + + if (unlikely(!qsvr || !qsvr->wildcard || iph->proto != IPPROTO_UDP + || svc->flags & DP_VS_SVC_F_PERSISTENT)) + return NULL; + + list_for_each_entry(dest, &svc->dests, n_list) { + if (quic_server_match(qsvr, dest)) { + found = true; + break; + } + } + if (!found || dest->fwdmode == DPVS_FWD_MODE_SNAT) + return NULL; + + ports = mbuf_header_pointer(mbuf, iph->len, sizeof(_ports), _ports); + if (unlikely(!ports)) + return NULL; + dp_vs_conn_fill_param(iph->af, iph->proto, + &iph->saddr, &iph->daddr, + ports[0], ports[1], + 0, ¶m); + + if (svc->flags & DP_VS_SVC_F_EXPIRE_QUIESCENT) + flags |= DP_VS_SVC_F_EXPIRE_QUIESCENT; + + conn = dp_vs_conn_new(mbuf, iph, ¶m, dest, flags); + if (!conn) + return NULL; + + dp_vs_stats_conn(conn); + return conn; +} diff --git a/src/ipvs/ip_vs_service.c b/src/ipvs/ip_vs_service.c index 44907dcd0..974f8527c 100644 --- a/src/ipvs/ip_vs_service.c +++ b/src/ipvs/ip_vs_service.c @@ -913,7 +913,8 @@ static int dp_vs_service_set(sockoptid_t opt, const void *user, size_t len) } if (usvc.proto != IPPROTO_TCP && usvc.proto != IPPROTO_UDP && - usvc.proto != IPPROTO_ICMP && usvc.proto != IPPROTO_ICMPV6) { + usvc.proto != IPPROTO_SCTP && usvc.proto != IPPROTO_ICMP && + usvc.proto != IPPROTO_ICMPV6) { RTE_LOG(ERR, SERVICE, "%s: protocol not support.\n", __func__); return EDPVS_INVAL; } diff --git a/src/ipvs/ip_vs_synproxy.c b/src/ipvs/ip_vs_synproxy.c index 902c4d55c..305b84f40 100644 --- a/src/ipvs/ip_vs_synproxy.c +++ b/src/ipvs/ip_vs_synproxy.c @@ -16,6 +16,9 @@ * */ #include +#include +#include +#include #include #include #include @@ -117,15 +120,35 @@ static int second_timer_expire(void *priv) } #endif +static int generate_random_key(void *key, unsigned length) +{ + int fd; + int ret; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return -1; + } + ret = read(fd, key, length); + close(fd); + + if (ret != (signed)length) { + return -1; + } + return 0; +} + int dp_vs_synproxy_init(void) { int i; char ack_mbufpool_name[32]; struct timeval tv; - for (i = 0; i < MD5_LBLOCK; i++) { - g_net_secret[0][i] = (uint32_t)random(); - g_net_secret[1][i] = (uint32_t)random(); + if (generate_random_key(g_net_secret, sizeof(g_net_secret))) { + for (i = 0; i < MD5_LBLOCK; i++) { + g_net_secret[0][i] = (uint32_t)random(); + g_net_secret[1][i] = (uint32_t)random(); + } } rte_atomic32_set(&g_minute_count, (uint32_t)random()); @@ -735,13 +758,14 @@ int dp_vs_synproxy_syn_rcv(int af, struct rte_mbuf *mbuf, } /* drop packet from blacklist */ - if (dp_vs_blklst_lookup(iph->af, iph->proto, &iph->daddr, - th->dest, &iph->saddr)) { + if (dp_vs_blklst_filtered(iph->af, iph->proto, &iph->daddr, + th->dest, &iph->saddr, mbuf)) { goto syn_rcv_out; } /* drop packet if not in whitelist */ - if (!dp_vs_whtlst_allow(iph->af, iph->proto, &iph->daddr, th->dest, &iph->saddr)) { + if (dp_vs_whtlst_filtered(iph->af, iph->proto, &iph->daddr, + th->dest, &iph->saddr, mbuf)) { goto syn_rcv_out; } } else { diff --git a/src/ipvs/ip_vs_whtlst.c b/src/ipvs/ip_vs_whtlst.c index bea11c922..36023e6ca 100644 --- a/src/ipvs/ip_vs_whtlst.c +++ b/src/ipvs/ip_vs_whtlst.c @@ -38,103 +38,257 @@ #define DPVS_WHTLST_TAB_BITS 16 #define DPVS_WHTLST_TAB_SIZE (1 << DPVS_WHTLST_TAB_BITS) #define DPVS_WHTLST_TAB_MASK (DPVS_WHTLST_TAB_SIZE - 1) - #define this_whtlst_tab (RTE_PER_LCORE(dp_vs_whtlst_tab)) #define this_num_whtlsts (RTE_PER_LCORE(num_whtlsts)) +#define DPVS_WHTLST_IPSET_TAB_BITS 8 +#define DPVS_WHTLST_IPSET_TAB_SIZE (1 << DPVS_WHTLST_IPSET_TAB_BITS) +#define DPVS_WHTLST_IPSET_TAB_MASK (DPVS_WHTLST_IPSET_TAB_SIZE - 1) +#define this_whtlst_ipset_tab (RTE_PER_LCORE(dp_vs_whtlst_ipset_tab)) +#define this_num_whtlsts_ipset (RTE_PER_LCORE(num_whtlsts_ipset)) + static RTE_DEFINE_PER_LCORE(struct list_head *, dp_vs_whtlst_tab); -static RTE_DEFINE_PER_LCORE(rte_atomic32_t, num_whtlsts); +static RTE_DEFINE_PER_LCORE(uint32_t, num_whtlsts); + +static RTE_DEFINE_PER_LCORE(struct list_head *, dp_vs_whtlst_ipset_tab); +static RTE_DEFINE_PER_LCORE(uint32_t, num_whtlsts_ipset); static uint32_t dp_vs_whtlst_rnd; -static inline uint32_t whtlst_hashkey(const uint8_t proto, const union inet_addr *vaddr, const uint16_t vport) +static inline void whtlst_fill_conf(const struct whtlst_entry *entry, + struct dp_vs_whtlst_conf *conf) +{ + memset(conf, 0, sizeof(*conf)); + conf->vaddr = entry->vaddr; + conf->vport = entry->vport; + conf->proto = entry->proto; + conf->af = entry->af; + conf->subject = entry->subject; + if (entry->set) + strncpy(conf->ipset, entry->set->name, sizeof(conf->ipset) - 1); +} + +static inline uint32_t whtlst_hashkey(const union inet_addr *vaddr, + uint16_t vport, bool ipset) { /* jhash hurts performance, we do not use rte_jhash_2words here */ - return (((rte_be_to_cpu_16(proto) * 7 - + rte_be_to_cpu_32(vaddr->in.s_addr)) * 31 - + rte_be_to_cpu_16(vport)) * 15 - + dp_vs_whtlst_rnd) & DPVS_WHTLST_TAB_MASK; + if (ipset) + return ((((vaddr->in.s_addr * 31) ^ vport) * 131) + ^ dp_vs_whtlst_rnd) & DPVS_WHTLST_IPSET_TAB_MASK; + + return ((((vaddr->in.s_addr * 31) ^ vport) * 131) + ^ dp_vs_whtlst_rnd) & DPVS_WHTLST_TAB_MASK; } -struct whtlst_entry *dp_vs_whtlst_lookup(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +static inline struct whtlst_entry *dp_vs_whtlst_ip_lookup( + int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject) { unsigned hashkey; - struct whtlst_entry *whtlst_node; - - hashkey = whtlst_hashkey(proto, vaddr, vport); - list_for_each_entry(whtlst_node, &this_whtlst_tab[hashkey], list){ - if (whtlst_node->af == af && whtlst_node->proto == proto && - whtlst_node->vport == vport && - inet_addr_equal(af, &whtlst_node->vaddr, vaddr) && - inet_addr_equal(af, &whtlst_node->whtlst, whtlst)) - return whtlst_node; + struct whtlst_entry *entry; + + hashkey = whtlst_hashkey(vaddr, vport, false); + list_for_each_entry(entry, &this_whtlst_tab[hashkey], list){ + if (entry->af == af && entry->proto == proto && + entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr) && + inet_addr_equal(af, &entry->subject, subject)) + return entry; } return NULL; } -bool dp_vs_whtlst_allow(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +static inline struct whtlst_entry *dp_vs_whtlst_ipset_lookup( + int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const char *ipset) { unsigned hashkey; - struct whtlst_entry *whtlst_node; + struct whtlst_entry *entry; - hashkey = whtlst_hashkey(proto, vaddr, vport); + hashkey = whtlst_hashkey(vaddr, vport, true); + list_for_each_entry(entry, &this_whtlst_ipset_tab[hashkey], list) { + if (entry->af == af && entry->proto == proto && entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr) && + !strncmp(entry->set->name, ipset, sizeof(entry->set->name))) + return entry; + } + + return NULL; +} + +static struct whtlst_entry *dp_vs_whtlst_lookup(const struct dp_vs_whtlst_conf *conf) +{ + struct whtlst_entry *entry; + + entry = dp_vs_whtlst_ip_lookup(conf->af, conf->proto, &conf->vaddr, conf->vport, + &conf->subject); + if (entry) + return entry; + + return dp_vs_whtlst_ipset_lookup(conf->af, conf->proto, &conf->vaddr, conf->vport, + conf->ipset); +} + +enum dpvs_whtlst_result { + DPVS_WHTLST_UNSET = 1, + DPVS_WHTLST_DENIED = 2, + DPVS_WHTLST_ALLOWED = 4, +}; + +static inline enum dpvs_whtlst_result dp_vs_whtlst_iplist_acl ( + int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject) +{ + bool set, hit; + unsigned hashkey; + struct whtlst_entry *entry; - if (&this_whtlst_tab[hashkey] == NULL || list_empty(&this_whtlst_tab[hashkey])) { - return true; + set = false; + hit = false; + + hashkey = whtlst_hashkey(vaddr, vport, false); + list_for_each_entry(entry, &this_whtlst_tab[hashkey], list){ + if (entry->af == af && entry->proto == proto && + entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr)) { + set = true; + hit = inet_addr_equal(af, &entry->subject, subject); + if (hit) + break; + } } - list_for_each_entry(whtlst_node, &this_whtlst_tab[hashkey], list){ - if (whtlst_node->af == af && whtlst_node->proto == proto && - whtlst_node->vport == vport && - inet_addr_equal(af, &whtlst_node->vaddr, vaddr) && - inet_addr_equal(af, &whtlst_node->whtlst, whtlst)) - return true; + + if (!set) + return DPVS_WHTLST_UNSET; + if (hit) + return DPVS_WHTLST_ALLOWED; + return DPVS_WHTLST_DENIED; +} + +static enum dpvs_whtlst_result dp_vs_whtlst_ipset_acl(int af, uint8_t proto, + const union inet_addr *vaddr, uint16_t vport, struct rte_mbuf *mbuf) +{ + bool set, hit; + unsigned hashkey; + struct whtlst_entry *entry; + + set = false; + hit = false; + + hashkey = whtlst_hashkey(vaddr, vport, true); + list_for_each_entry(entry, &this_whtlst_ipset_tab[hashkey], list) { + if (entry->af == af && entry->proto == proto && + entry->vport == vport && + inet_addr_equal(af, &entry->vaddr, vaddr)) { + set = true; + rte_pktmbuf_prepend(mbuf, mbuf->l2_len); + hit = elem_in_set(entry->set, mbuf, entry->dst_match); + rte_pktmbuf_adj(mbuf, mbuf->l2_len); + if (hit) + break; + } } - return false; + if (!set) + return DPVS_WHTLST_UNSET; + if (hit) + return DPVS_WHTLST_ALLOWED; + return DPVS_WHTLST_DENIED; } -static int dp_vs_whtlst_add_lcore(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +bool dp_vs_whtlst_filtered(int af, uint8_t proto, const union inet_addr *vaddr, + uint16_t vport, const union inet_addr *subject, struct rte_mbuf *mbuf) +{ + /* + * The tri-state combinations for ipset and iplist: + * ---------------------------------------------------- + * ipset\iplist | UNSET ALLOWED DENIED + * -------------|-------------------------------------- + * UNSET | Accept Accept Reject + * ALLOWED | Accept Accept Accept + * DENIED | Reject Accept Reject + * ---------------------------------------------------- + * + * Notes: + * - Once attached to service, the empty ipset whtlst entry denies all. + * The UNSET means no matched entries attached to the service. + * - If a client is allowed in either iplist or ipset, but denied in + * the other, then the client is allowed. + */ + enum dpvs_whtlst_result res1, res2; + + res1 = dp_vs_whtlst_iplist_acl(af, proto, vaddr, vport, subject); + res2 = dp_vs_whtlst_ipset_acl(af, proto, vaddr, vport, mbuf); + + return !((DPVS_WHTLST_ALLOWED == res1) || ( DPVS_WHTLST_ALLOWED == res2) + || ((DPVS_WHTLST_UNSET == res1) && (DPVS_WHTLST_UNSET == res2))); +} + +static int dp_vs_whtlst_add_lcore(const struct dp_vs_whtlst_conf *conf) { unsigned hashkey; - struct whtlst_entry *new, *whtlst_node; - whtlst_node = dp_vs_whtlst_lookup(af, proto, vaddr, vport, whtlst); - if (whtlst_node) { + struct whtlst_entry *new; + bool is_ipset = conf->ipset[0] != '\0'; + + if (dp_vs_whtlst_lookup(conf)) return EDPVS_EXIST; - } - hashkey = whtlst_hashkey(proto, vaddr, vport); + hashkey = whtlst_hashkey(&conf->vaddr, conf->vport, is_ipset); new = rte_zmalloc("new_whtlst_entry", sizeof(struct whtlst_entry), 0); if (unlikely(new == NULL)) return EDPVS_NOMEM; - new->af = af; - new->vport = vport; - new->proto = proto; - memcpy(&new->vaddr, vaddr, sizeof(union inet_addr)); - memcpy(&new->whtlst, whtlst, sizeof(union inet_addr)); - list_add(&new->list, &this_whtlst_tab[hashkey]); - rte_atomic32_inc(&this_num_whtlsts); + new->vaddr = conf->vaddr; + new->vport = conf->vport; + new->proto = conf->proto; + new->af = conf->af; + + if (is_ipset) { + new->set = ipset_get(conf->ipset); + if (!new->set) { + RTE_LOG(ERR, SERVICE, "[%2d] %s: ipset %s not found\n", + rte_lcore_id(), __func__, conf->ipset); + rte_free(new); + return EDPVS_INVAL; + } + // Notes: Reassess it when new ipset types added! + if (!strcmp(new->set->type->name, "hash:ip,port,net") || + !strcmp(new->set->type->name, "hash:ip,port,ip") || + !strcmp(new->set->type->name, "hash:net,port,net")) + new->dst_match = true; + else + new->dst_match = false; + list_add(&new->list, &this_whtlst_ipset_tab[hashkey]); + ++this_num_whtlsts_ipset; + } else { + new->subject = conf->subject; + list_add(&new->list, &this_whtlst_tab[hashkey]); + ++this_num_whtlsts; + } return EDPVS_OK; } -static int dp_vs_whtlst_del_lcore(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +static int dp_vs_whtlst_del_lcore(const struct dp_vs_whtlst_conf *conf) { - struct whtlst_entry *whtlst_node; + struct whtlst_entry *entry; - whtlst_node = dp_vs_whtlst_lookup(af, proto, vaddr, vport, whtlst); - if (whtlst_node != NULL) { - list_del(&whtlst_node->list); - rte_free(whtlst_node); - rte_atomic32_dec(&this_num_whtlsts); - return EDPVS_OK; + entry = dp_vs_whtlst_lookup(conf); + if (!entry) + return EDPVS_NOTEXIST; + + if (entry->set) { /* ipset entry */ + list_del(&entry->list); + ipset_put(entry->set); + --this_num_whtlsts_ipset; + } else { + list_del(&entry->list); + --this_num_whtlsts; } - return EDPVS_NOTEXIST; + + rte_free(entry); + return EDPVS_OK; } static uint32_t whtlst_msg_seq(void) @@ -143,42 +297,33 @@ static uint32_t whtlst_msg_seq(void) return counter++; } -static int dp_vs_whtlst_add(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +static int dp_vs_whtlst_add(const struct dp_vs_whtlst_conf *conf) { lcoreid_t cid = rte_lcore_id(); int err; struct dpvs_msg *msg; - struct dp_vs_whtlst_conf cf; if (cid != rte_get_main_lcore()) { - RTE_LOG(INFO, SERVICE, "[%s] must set from master lcore\n", __func__); + RTE_LOG(INFO, SERVICE, "%s must set from master lcore\n", __func__); return EDPVS_NOTSUPP; } - memset(&cf, 0, sizeof(struct dp_vs_whtlst_conf)); - memcpy(&(cf.vaddr), vaddr, sizeof(union inet_addr)); - memcpy(&(cf.whtlst), whtlst, sizeof(union inet_addr)); - cf.af = af; - cf.vport = vport; - cf.proto = proto; - - /*set whtlst ip on master lcore*/ - err = dp_vs_whtlst_add_lcore(af, proto, vaddr, vport, whtlst); + /* master lcore */ + err = dp_vs_whtlst_add_lcore(conf); if (err && err != EDPVS_EXIST) { - RTE_LOG(ERR, SERVICE, "[%s] fail to set whtlst ip\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: fail to add whtlst entry -- %s\n", __func__, dpvs_strerror(err)); return err; } - /*set whtlst ip on all slave lcores*/ + /* slave lcores */ msg = msg_make(MSG_TYPE_WHTLST_ADD, whtlst_msg_seq(), DPVS_MSG_MULTICAST, - cid, sizeof(struct dp_vs_whtlst_conf), &cf); + cid, sizeof(struct dp_vs_whtlst_conf), conf); if (!msg) return EDPVS_NOMEM; err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL); if (err != EDPVS_OK) { msg_destroy(&msg); - RTE_LOG(INFO, SERVICE, "[%s] fail to send multicast message\n", __func__); + RTE_LOG(INFO, SERVICE, "%s: fail to send multicast message\n", __func__); return err; } msg_destroy(&msg); @@ -186,41 +331,33 @@ static int dp_vs_whtlst_add(int af, uint8_t proto, const union inet_addr *vaddr, return EDPVS_OK; } -static int dp_vs_whtlst_del(int af, uint8_t proto, const union inet_addr *vaddr, - uint16_t vport, const union inet_addr *whtlst) +static int dp_vs_whtlst_del(const struct dp_vs_whtlst_conf *conf) { lcoreid_t cid = rte_lcore_id(); int err; struct dpvs_msg *msg; - struct dp_vs_whtlst_conf cf; if (cid != rte_get_main_lcore()) { - RTE_LOG(INFO, SERVICE, "[%s] must set from master lcore\n", __func__); + RTE_LOG(INFO, SERVICE, "%s must set from master lcore\n", __func__); return EDPVS_NOTSUPP; } - memset(&cf, 0, sizeof(struct dp_vs_whtlst_conf)); - memcpy(&(cf.vaddr), vaddr, sizeof(union inet_addr)); - memcpy(&(cf.whtlst), whtlst, sizeof(union inet_addr)); - cf.af = af; - cf.vport = vport; - cf.proto = proto; - - /*del whtlst ip on master lcores*/ - err = dp_vs_whtlst_del_lcore(af, proto, vaddr, vport, whtlst); + /* master lcore */ + err = dp_vs_whtlst_del_lcore(conf); if (err) { + RTE_LOG(ERR, SERVICE, "%s: fail to del whtlst entry -- %s\n", __func__, dpvs_strerror(err)); return err; } - /*del whtlst ip on all slave lcores*/ + /* slave lcores */ msg = msg_make(MSG_TYPE_WHTLST_DEL, whtlst_msg_seq(), DPVS_MSG_MULTICAST, - cid, sizeof(struct dp_vs_whtlst_conf), &cf); + cid, sizeof(struct dp_vs_whtlst_conf), conf); if (!msg) return EDPVS_NOMEM; err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL); if (err != EDPVS_OK) { msg_destroy(&msg); - RTE_LOG(INFO, SERVICE, "[%s] fail to send multicast message\n", __func__); + RTE_LOG(INFO, SERVICE, "%s: fail to send multicast message\n", __func__); return err; } msg_destroy(&msg); @@ -228,36 +365,56 @@ static int dp_vs_whtlst_del(int af, uint8_t proto, const union inet_addr *vaddr, return EDPVS_OK; } -void dp_vs_whtlst_flush(struct dp_vs_service *svc) +void dp_vs_whtlst_flush(struct dp_vs_service *svc) { - struct whtlst_entry *entry, *next; int hash; + struct whtlst_entry *entry, *next; + struct dp_vs_whtlst_conf conf; for (hash = 0; hash < DPVS_WHTLST_TAB_SIZE; hash++) { list_for_each_entry_safe(entry, next, &this_whtlst_tab[hash], list) { if (entry->af == svc->af - && entry->vport == svc->port - && entry->proto == svc->proto - && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) - dp_vs_whtlst_del(svc->af, entry->proto, &entry->vaddr, - entry->vport, &entry->whtlst); + && entry->vport == svc->port + && entry->proto == svc->proto + && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) { + whtlst_fill_conf(entry, &conf); + dp_vs_whtlst_del(&conf); + } + } + } + + for (hash = 0; hash < DPVS_WHTLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry_safe(entry, next, &this_whtlst_ipset_tab[hash], list) { + if (entry->af == svc->af + && entry->vport == svc->port + && entry->proto == svc->proto + && inet_addr_equal(svc->af, &entry->vaddr, &svc->addr)) { + whtlst_fill_conf(entry, &conf); + dp_vs_whtlst_del(&conf); + } } } - return; } static void dp_vs_whtlst_flush_all(void) { - struct whtlst_entry *entry, *next; int hash; + struct whtlst_entry *entry, *next; + struct dp_vs_whtlst_conf conf; for (hash = 0; hash < DPVS_WHTLST_TAB_SIZE; hash++) { list_for_each_entry_safe(entry, next, &this_whtlst_tab[hash], list) { - dp_vs_whtlst_del(entry->af, entry->proto, &entry->vaddr, - entry->vport, &entry->whtlst); + whtlst_fill_conf(entry, &conf); + dp_vs_whtlst_del(&conf); + } + } + + for (hash = 0; hash < DPVS_WHTLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry_safe(entry, next, &this_whtlst_ipset_tab[hash], list) { + whtlst_fill_conf(entry, &conf); + dp_vs_whtlst_del(&conf); } } - return; } /* @@ -265,40 +422,19 @@ static void dp_vs_whtlst_flush_all(void) */ static int whtlst_sockopt_set(sockoptid_t opt, const void *conf, size_t size) { - const struct dp_vs_whtlst_conf *whtlst_conf = conf; - int err; - - if (!conf && size < sizeof(*whtlst_conf)) + if (!conf && size < sizeof(struct dp_vs_whtlst_conf)) return EDPVS_INVAL; switch (opt) { case SOCKOPT_SET_WHTLST_ADD: - err = dp_vs_whtlst_add(whtlst_conf->af, - whtlst_conf->proto, &whtlst_conf->vaddr, - whtlst_conf->vport, &whtlst_conf->whtlst); - break; + return dp_vs_whtlst_add(conf); case SOCKOPT_SET_WHTLST_DEL: - err = dp_vs_whtlst_del(whtlst_conf->af, - whtlst_conf->proto, &whtlst_conf->vaddr, - whtlst_conf->vport, &whtlst_conf->whtlst); - break; + return dp_vs_whtlst_del(conf); default: - err = EDPVS_NOTSUPP; - break; + return EDPVS_NOTSUPP; } - return err; -} - -static void whtlst_fill_conf(struct dp_vs_whtlst_conf *cf, - const struct whtlst_entry *entry) -{ - memset(cf, 0 ,sizeof(*cf)); - cf->af = entry->af; - cf->vaddr = entry->vaddr; - cf->whtlst = entry->whtlst; - cf->proto = entry->proto; - cf->vport = entry->vport; + return EDPVS_OK; } static int whtlst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, @@ -309,7 +445,7 @@ static int whtlst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, size_t naddr, hash; int off = 0; - naddr = rte_atomic32_read(&this_num_whtlsts); + naddr = this_num_whtlsts + this_num_whtlsts_ipset; *outsize = sizeof(struct dp_vs_whtlst_conf_array) + naddr * sizeof(struct dp_vs_whtlst_conf); *out = rte_calloc(NULL, 1, *outsize, 0); @@ -322,7 +458,15 @@ static int whtlst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, list_for_each_entry(entry, &this_whtlst_tab[hash], list) { if (off >= naddr) break; - whtlst_fill_conf(&array->whtlsts[off++], entry); + whtlst_fill_conf(entry, &array->whtlsts[off++]); + } + } + + for (hash = 0; hash < DPVS_WHTLST_IPSET_TAB_SIZE; hash++) { + list_for_each_entry(entry, &this_whtlst_ipset_tab[hash], list) { + if (off >= naddr) + break; + whtlst_fill_conf(entry, &array->whtlsts[off++]); } } @@ -332,35 +476,35 @@ static int whtlst_sockopt_get(sockoptid_t opt, const void *conf, size_t size, static int whtlst_msg_process(bool add, struct dpvs_msg *msg) { - struct dp_vs_whtlst_conf *cf; + struct dp_vs_whtlst_conf *conf; int err; assert(msg); if (msg->len != sizeof(struct dp_vs_whtlst_conf)) { - RTE_LOG(ERR, SERVICE, "%s: bad message.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: bad message\n", __func__); return EDPVS_INVAL; } - cf = (struct dp_vs_whtlst_conf *)msg->data; + conf = (struct dp_vs_whtlst_conf *)msg->data; if (add) { - err = dp_vs_whtlst_add_lcore(cf->af, cf->proto, &cf->vaddr, cf->vport, &cf->whtlst); - if (err && err != EDPVS_EXIST) { - RTE_LOG(ERR, SERVICE, "%s: fail to add whtlst: %s.\n", __func__, dpvs_strerror(err)); - } - } - else { - err = dp_vs_whtlst_del_lcore(cf->af, cf->proto, &cf->vaddr, cf->vport, &cf->whtlst); + err = dp_vs_whtlst_add_lcore(conf); + if (err && err != EDPVS_EXIST) + RTE_LOG(ERR, SERVICE, "%s: fail to add whtlst: %s\n", __func__, dpvs_strerror(err)); + } else { + err = dp_vs_whtlst_del_lcore(conf); + if (err && err != EDPVS_NOTEXIST) + RTE_LOG(ERR, SERVICE, "%s: fail to del whtlst: %s\n", __func__, dpvs_strerror(err)); } return err; } -inline static int whtlst_add_msg_cb(struct dpvs_msg *msg) +static inline int whtlst_add_msg_cb(struct dpvs_msg *msg) { return whtlst_msg_process(true, msg); } -inline static int whtlst_del_msg_cb(struct dpvs_msg *msg) +static inline int whtlst_del_msg_cb(struct dpvs_msg *msg) { return whtlst_msg_process(false, msg); } @@ -378,23 +522,45 @@ static struct dpvs_sockopts whtlst_sockopts = { static int whtlst_lcore_init(void *args) { int i; - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; + + this_num_whtlsts = 0; + this_num_whtlsts_ipset = 0; + this_whtlst_tab = rte_malloc(NULL, sizeof(struct list_head) * DPVS_WHTLST_TAB_SIZE, RTE_CACHE_LINE_SIZE); if (!this_whtlst_tab) return EDPVS_NOMEM; - for (i = 0; i < DPVS_WHTLST_TAB_SIZE; i++) INIT_LIST_HEAD(&this_whtlst_tab[i]); + this_whtlst_ipset_tab = rte_malloc(NULL, sizeof(struct list_head) * + DPVS_WHTLST_IPSET_TAB_SIZE, RTE_CACHE_LINE_SIZE); + if (!this_whtlst_ipset_tab) { + rte_free(this_whtlst_tab); + return EDPVS_NOMEM; + } + for (i = 0; i < DPVS_WHTLST_IPSET_TAB_SIZE; i++) + INIT_LIST_HEAD(&this_whtlst_ipset_tab[i]); + return EDPVS_OK; } static int whtlst_lcore_term(void *args) { - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; dp_vs_whtlst_flush_all(); @@ -403,13 +569,19 @@ static int whtlst_lcore_term(void *args) rte_free(this_whtlst_tab); this_whtlst_tab = NULL; } + + if (this_whtlst_ipset_tab) { + rte_free(this_whtlst_ipset_tab); + this_whtlst_ipset_tab = NULL; + } + return EDPVS_OK; } -static int whtlst_unregister_msg_cb(void) +static void whtlst_unregister_msg_cb(void) { - struct dpvs_msg_type msg_type; int err; + struct dpvs_msg_type msg_type; memset(&msg_type, 0, sizeof(struct dpvs_msg_type)); msg_type.type = MSG_TYPE_WHTLST_ADD; @@ -419,8 +591,8 @@ static int whtlst_unregister_msg_cb(void) msg_type.unicast_msg_cb = whtlst_add_msg_cb; err = msg_type_mc_unregister(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to unregister msg.\n", __func__); - return err; + RTE_LOG(WARNING, SERVICE, "%s: unregister WHTLST_ADD msg failed -- %s\n", + __func__, dpvs_strerror(err)); } memset(&msg_type, 0, sizeof(struct dpvs_msg_type)); @@ -431,10 +603,9 @@ static int whtlst_unregister_msg_cb(void) msg_type.unicast_msg_cb = whtlst_del_msg_cb; err = msg_type_mc_unregister(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to unregister msg.\n", __func__); - return err; + RTE_LOG(WARNING, SERVICE, "%s: unregister WHTLIST_DEL msg failed -- %s\n", + __func__, dpvs_strerror(err)); } - return EDPVS_OK; } int dp_vs_whtlst_init(void) @@ -443,13 +614,11 @@ int dp_vs_whtlst_init(void) lcoreid_t cid; struct dpvs_msg_type msg_type; - rte_atomic32_set(&this_num_whtlsts, 0); - rte_eal_mp_remote_launch(whtlst_lcore_init, NULL, CALL_MAIN); RTE_LCORE_FOREACH_WORKER(cid) { if ((err = rte_eal_wait_lcore(cid)) < 0) { - RTE_LOG(WARNING, SERVICE, "%s: lcore %d: %s.\n", - __func__, cid, dpvs_strerror(err)); + RTE_LOG(WARNING, SERVICE, "[%02d] %s: whtlst init failed -- %s\n", + cid, __func__, dpvs_strerror(err)); return err; } } @@ -462,7 +631,8 @@ int dp_vs_whtlst_init(void) msg_type.unicast_msg_cb = whtlst_add_msg_cb; err = msg_type_mc_register(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to register msg.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: register WHTLST_ADD msg failed -- %s\n", + __func__, dpvs_strerror(err)); return err; } @@ -474,14 +644,18 @@ int dp_vs_whtlst_init(void) msg_type.unicast_msg_cb = whtlst_del_msg_cb; err = msg_type_mc_register(&msg_type); if (err != EDPVS_OK) { - RTE_LOG(ERR, SERVICE, "%s: fail to register msg.\n", __func__); + RTE_LOG(ERR, SERVICE, "%s: register WHTLST_DEL msg failed -- %s\n", + __func__, dpvs_strerror(err)); return err; } if ((err = sockopt_register(&whtlst_sockopts)) != EDPVS_OK) { + RTE_LOG(ERR, SERVICE, "%s: register sockopts failed -- %s\n", + __func__, dpvs_strerror(err)); whtlst_unregister_msg_cb(); return err; } + dp_vs_whtlst_rnd = (uint32_t)random(); return EDPVS_OK; @@ -492,17 +666,18 @@ int dp_vs_whtlst_term(void) int err; lcoreid_t cid; - if ((err = whtlst_unregister_msg_cb()) != EDPVS_OK) - return err; + if ((err = sockopt_unregister(&whtlst_sockopts)) != EDPVS_OK) { + RTE_LOG(WARNING, SERVICE, "%s: unregister sockopts failed -- %s\n", + __func__, dpvs_strerror(err)); + } - if ((err = sockopt_unregister(&whtlst_sockopts)) != EDPVS_OK) - return err; + whtlst_unregister_msg_cb(); rte_eal_mp_remote_launch(whtlst_lcore_term, NULL, CALL_MAIN); RTE_LCORE_FOREACH_WORKER(cid) { if ((err = rte_eal_wait_lcore(cid)) < 0) { - RTE_LOG(WARNING, SERVICE, "%s: lcore %d: %s.\n", - __func__, cid, dpvs_strerror(err)); + RTE_LOG(WARNING, SERVICE, "[%02d] %s: whtlst termination failed -- %s\n", + cid, __func__, dpvs_strerror(err)); } } diff --git a/src/kni.c b/src/kni.c index 6da4799e5..185371d42 100644 --- a/src/kni.c +++ b/src/kni.c @@ -34,7 +34,7 @@ #include "conf/common.h" #include "dpdk.h" #include "netif.h" -#include "netif_addr.h" +#include "conf/netif_addr.h" #include "ctrl.h" #include "kni.h" #include "vlan.h" @@ -108,7 +108,7 @@ static int kni_mc_list_cmp_set(struct netif_port *dev, rte_rwlock_write_lock(&dev->dev_lock); naddr_old = NELEMS(addrs_old); - err = __netif_mc_dump(dev, addrs_old, &naddr_old); + err = __netif_mc_dump(dev, HW_ADDR_F_FROM_KNI, addrs_old, &naddr_old); if (err != EDPVS_OK) { RTE_LOG(ERR, Kni, "%s: fail to get current mc list\n", __func__); goto out; @@ -162,14 +162,14 @@ static int kni_mc_list_cmp_set(struct netif_port *dev, /* nothing */ break; case 1: - err = __netif_mc_add(dev, &chg_lst.addrs[i]); + err = __netif_hw_addr_add(&dev->mc, &chg_lst.addrs[i], HW_ADDR_F_FROM_KNI); RTE_LOG(INFO, Kni, "%s: add mc addr: %s %s %s\n", __func__, eth_addr_dump(&chg_lst.addrs[i], mac, sizeof(mac)), dev->name, dpvs_strerror(err)); break; case 2: - err = __netif_mc_del(dev, &chg_lst.addrs[i]); + err = __netif_hw_addr_del(&dev->mc, &chg_lst.addrs[i], HW_ADDR_F_FROM_KNI); RTE_LOG(INFO, Kni, "%s: del mc addr: %s %s %s\n", __func__, eth_addr_dump(&chg_lst.addrs[i], mac, sizeof(mac)), @@ -246,7 +246,7 @@ static int kni_rtnl_check(void *arg) { struct netif_port *dev = arg; int fd = dev->kni.kni_rtnl_fd; - int n, i; + int n, i, link_flags = 0; char buf[4096]; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; bool update = false; @@ -284,6 +284,14 @@ static int kni_rtnl_check(void *arg) /* note we should not update kni mac list for every event ! */ if (update) { RTE_LOG(DEBUG, Kni, "%d events received!\n", i); + if (EDPVS_OK != linux_get_link_status(dev->kni.name, &link_flags, NULL, 0)) { + RTE_LOG(ERR, Kni, "%s:undetermined kni link status\n", dev->kni.name); + return DTIMER_OK; + } + if (!(link_flags & IFF_UP)) { + RTE_LOG(DEBUG, Kni, "skip link down kni device %s\n", dev->kni.name); + return DTIMER_OK; + } if (kni_update_maddr(dev) == EDPVS_OK) RTE_LOG(DEBUG, Kni, "update maddr of %s OK!\n", dev->name); else diff --git a/src/lldp.c b/src/lldp.c new file mode 100644 index 000000000..26c4953cb --- /dev/null +++ b/src/lldp.c @@ -0,0 +1,1902 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * Jul 2024, yuwenchao@qiyi.com, Initial + */ + +#include +#include +#include "list.h" +#include "timer.h" +#include "ctrl.h" +#include "netif.h" +#include "netif_addr.h" +#include "lldp.h" +#include "conf/lldp.h" + +#define RTE_LOGTYPE_LLDP RTE_LOGTYPE_USER1 + +#define DPVS_LLDP_PDU_MAX 1500 +#define DPVS_LLDP_TTL_DEFAULT 120 +#define DPVS_LLDP_TX_INTERVAL 30 +#define DPVS_LLDP_UPDATE_INTERVAL 600 + +#define DPVS_LLDP_TL_TYPE(tl) ((rte_be_to_cpu_16(tl) & 0xfe00) >> 9) +#define DPVS_LLDP_TL_LEN(tl) ((rte_be_to_cpu_16(tl) & 0x01ff)) +#define DPVS_LLDP_TL(type, len) (rte_cpu_to_be_16((((type) & 0x7f) << 9) | ((len) & 0x1ff))) + +#define lldp_type_equal(t1, t2) (((t1).type == (t2).type) && ((t1).subtype == (t2).subtype)) + +/* helper macro used in lldp_type_ops::dump + * @buf: target string buffer, must be an array + * @pos: start position for this snprintf, must be an initialized integer variable + * */ +#define lldp_dump_snprintf(buf, pos, fmt, ...) \ + do { \ + int res = snprintf(&(buf)[pos], sizeof(buf) - pos, fmt, ##__VA_ARGS__); \ + if (unlikely(res < 0)) \ + return EDPVS_IO; \ + (pos) += res; \ + if ((pos) >= sizeof(buf)) \ + return EDPVS_NOROOM; \ + } while (0) + +/* helper macro used ihn lldp_type_ops::dump + * @buf: target string buffer, must be an array + * @pos: start position for this snprintf, must be an initialized integer variable + * @s: non-null-terminated string (use lldp_dump_snprintf for null-terminated string) + * @n: length of s + * @ends: ending string appended into buf + * */ +#define lldp_dump_strcpy(buf, pos, s, n, ends) \ + do { \ + int i, endslen = strlen(ends); \ + if (unlikely((endslen + (n)) >= (sizeof(buf) - (pos)))) \ + return EDPVS_NOROOM; \ + rte_memcpy(&(buf)[pos], s, n); \ + (pos) += (n); \ + for (i = 0; i < endslen; i++) \ + (buf)[(pos)++] = ends[i]; \ + (buf)[pos] = '\0'; \ + } while (0) + +const struct rte_ether_addr LLDP_ETHER_ADDR_DST = { + .addr_bytes = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E} +}; + +/* + * LLDP is processed only on master lcore, all data structures are free of lock + */ + +typedef struct { + uint8_t type; + uint32_t subtype; +} lldp_type_t; + +struct lldp_port { + struct netif_port *dev; + struct list_head head; /* lldp_entry list head, sorted by lldp type */ + struct list_head node; + struct dpvs_timer timer; + uint32_t timeout; + uint16_t entries; + uint16_t neigh; /* DPVS_LLDP_NODE_xxx */ +}; + +struct lldp_entry { + struct list_head node; + struct lldp_port *port; + uint8_t stale; + lldp_type_t type; + uint16_t len; /* host endian */ + + /* lldp pdu */ + uint16_t typelen; /* network endian */ + char value[0]; +}; + +struct lldp_type_ops { + uint8_t type; + + /* + * Parse LLDP type and subtype from LLDP PDU + * @params + * llpdu: lldp pdu + * type: where to store the parsed type id, must not be NULL + * len: where to store the parse data len for the type, can be NULL + * @return + * DPVS error code num + * */ + int (*parse_type)(const char *llpdu, lldp_type_t *type, uint16_t *len); + + /* + * Generate LLDP PDU, and store it to lldpdu + * @params + * dev: physical netif port + * subtype: subtype of the LLDP PDU + * lldpdu: lldp pdu buffer + * len: buffer size + * @return + * the lldp pdu length on success or buffer not big enough + * dpvs negative error code on error + * */ + int (*local_lldp)(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len); + + /* + * Translate LLDP PDU, store the translated message into buf. + * @params + * e: lldp entry + * buf: message buffer + * len: message buffer size + * @return (similar to "snprintf") + * the message length on success or buffer not big enough + * negative error code on error + * the returned message always teminates with '\0'. + * */ + int (*dump)(const struct lldp_entry *e, char *buf, size_t len); + + /* + * Actions to take after lldp pdu changed (add, update) + * @params: + * entry: the newly added entry + * @return + * dpvs error code + * */ + int (*on_change)(const struct lldp_entry *entry); +}; + +static int lldp_enable = 0; +static struct dpvs_timer lldp_xmit_timer; +static struct dpvs_timer lldp_update_timer; + +static char lldp_sn[256]; +static struct utsname lldp_uname; + +static struct list_head lldp_ports[DPVS_LLDP_NODE_MAX]; +static struct lldp_type_ops *lldp_types[DPVS_LLDP_TYPE_MAX] = { NULL }; + +static int lldp_xmit_start(void); +static int lldp_xmit_stop(void); + +void dpvs_lldp_enable(void) +{ + int err; + + if (lldp_enable) + return; + + if (dpvs_state_get() == DPVS_STATE_NORMAL) { + if ((err = lldp_xmit_start()) != EDPVS_OK) { + RTE_LOG(ERR, LLDP, "%s: fail to enable lldp -- %s\n", + __func__, dpvs_strerror(err)); + return; + } + } + + lldp_enable = 1; +} + +void dpvs_lldp_disable(void) +{ + int err; + + if (!lldp_enable) + return; + + if (dpvs_state_get() == DPVS_STATE_NORMAL) { + if ((err = lldp_xmit_stop()) != EDPVS_OK) { + RTE_LOG(ERR, LLDP, "%s: fail to disable lldp -- %s\n", + __func__, dpvs_strerror(err)); + return; + } + } + + lldp_enable = 0; +} + +bool dpvs_lldp_is_enabled(void) +{ + return !!lldp_enable; +} + +static int lldp_serail_number_init(void) +{ + FILE *fp; + char *ptr; + + fp = fopen("/sys/class/dmi/id/product_serial", "r"); + if (!fp) { + RTE_LOG(WARNING, LLDP, "%s: fail to open serial number file\n", __func__); + snprintf(lldp_sn, sizeof(lldp_sn), "%s", "Unknown"); + return EDPVS_SYSCALL; + } + + if (!fgets(lldp_sn, sizeof(lldp_sn), fp)) { + RTE_LOG(WARNING, LLDP, "%s: fail to read serial number file\n", __func__); + snprintf(lldp_sn, sizeof(lldp_sn), "%s", "Unknown"); + return EDPVS_IO; + } + + /* remove the tailing LF character */ + ptr = strrchr(lldp_sn, '\n'); + if (ptr) + *ptr = '\0'; + + return EDPVS_OK; +} + +static inline int lldp_type_cmp(lldp_type_t *t1, lldp_type_t *t2) +{ + if (t1->type < t2->type) + return -1; + if (t1->type > t2->type) + return 1; + if (t1->subtype < t2->subtype) + return -1; + if (t1->subtype > t2->subtype) + return 1; + return 0; +} + +static int lldp_type_register(struct lldp_type_ops *ops) +{ + if (!ops || ops->type >= DPVS_LLDP_TYPE_MAX) + return EDPVS_INVAL; + + if (lldp_types[ops->type] != NULL) + return EDPVS_EXIST; + + if (!ops->parse_type || !ops->dump) + return EDPVS_INVAL; + + lldp_types[ops->type] = ops; + return EDPVS_OK; +} + +static int lldp_type_unregister(struct lldp_type_ops *ops) +{ + if (!ops || ops->type >= DPVS_LLDP_TYPE_MAX) + return EDPVS_INVAL; + + if (!lldp_types[ops->type]) + return EDPVS_NOTEXIST; + + lldp_types[ops->type] = NULL; + return EDPVS_OK; +} + +static struct lldp_type_ops *lldp_type_get(lldp_type_t type) +{ + if (type.type >= DPVS_LLDP_TYPE_MAX) + return NULL; + return lldp_types[type.type]; +} + +static int lldp_parse_type_default(const char *lldpdu, lldp_type_t *type, uint16_t *len) +{ + assert(NULL != type); + + type->type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + type->subtype = 0; + if (!LLDP_TYPE_VALID(type->type)) { + type->type = 0; + return EDPVS_INVAL; + } + if (len) + *len = DPVS_LLDP_TL_LEN(*((uint16_t *)lldpdu)); + + return EDPVS_OK; +} + +static int lldp_local_pdu_end(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + uint16_t *typelen = (uint16_t *)buf; + + if (len >= 2) + *typelen = DPVS_LLDP_TL(LLDP_TYPE_END, 0); + else + memset(buf, 0, len); + return 2; +} + +static int lldp_dump_end(const struct lldp_entry *e, char *buf, size_t len) +{ + return snprintf(buf, len, "%s\n", "End of LLDPDU TLV"); +} + +static int lldp_parse_type_chassis_id(const char *lldpdu, lldp_type_t *type, uint16_t *len) +{ + assert(type != NULL); + + type->type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + if (!LLDP_TYPE_VALID(type->type)) { + type->type = 0; + return EDPVS_INVAL; + } + + type->subtype = *(lldpdu + 2); + if (!LLDP_CHASSIS_ID_VALID(type->subtype)) { + type->subtype = 0; + return EDPVS_INVAL; + } + + if (len) + *len = DPVS_LLDP_TL_LEN(*((uint16_t *)lldpdu)); + + return EDPVS_OK; +} + +static int lldp_local_pdu_chassis_id(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + if (len >= 2 + 7) { + *((uint16_t *)buf) = DPVS_LLDP_TL(LLDP_TYPE_CHASSIS_ID, 7); + buf[2] = LLDP_CHASSIS_ID_MAC_ADDRESS; + rte_memcpy(&buf[3], &dev->addr, 6); + } else { + memset(buf, 0, len); + } + return 2 + 7; +} + +static int lldp_dump_chassis_id(const struct lldp_entry *e, char *buf, size_t len) +{ + const uint8_t *ptr = (const uint8_t *)e->value; /* Chassis ID Type */ + int pos = 0; + char tbuf[512], ipbuf[64]; + + lldp_dump_snprintf(tbuf, pos, "%s (%d)\n", "Chassis ID TLV", e->type.type); + + assert(e->type.subtype == *ptr); + ++ptr; /* Chassis ID Data */ + switch (e->type.subtype) { + case LLDP_CHASSIS_ID_CHASSIS_COMPONENT: + lldp_dump_snprintf(tbuf, pos, "%s", "\tChassis Component: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_CHASSIS_ID_INTERFACE_ALIAS: + lldp_dump_snprintf(tbuf, pos, "%s", "\tInterface Alias: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_CHASSIS_ID_PORT_COMPONENT: + lldp_dump_snprintf(tbuf, pos, "%s", "\tPort Component: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_CHASSIS_ID_MAC_ADDRESS: + if (unlikely(e->len < 7)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tMAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); + break; + case LLDP_CHASSIS_ID_NETWORK_ADDRESS: + switch (*ptr) { + case LLDP_ADDR_IPV4: + if (unlikely(e->len < 6)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv4: %s\n", inet_ntop(AF_INET, ptr + 1, + ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + case LLDP_ADDR_IPV6: + if (unlikely(e->len < 18)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv6: %s\n", inet_ntop(AF_INET6, ptr + 1, + ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + default: + if (unlikely(e->len <= 2)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tNetwork Address Type %d:", *ptr); + pos += binary2hexstr(ptr + 1, e->len - 2, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + } + break; + case LLDP_CHASSIS_ID_INTERFACE_NAME: + lldp_dump_snprintf(tbuf, pos, "%s", "\tInterface Name: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_CHASSIS_ID_LOCALLY_ASSIGNED: + lldp_dump_snprintf(tbuf, pos, "%s", "\tLocal: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + default: + lldp_dump_snprintf(tbuf, pos, "\t%s: ", "Bad Chassis ID"); + pos += binary2print(ptr, e->len - 1, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + } + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static int lldp_parse_type_port_id(const char *lldpdu, lldp_type_t *type, uint16_t *len) +{ + assert(type != NULL); + + type->type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + if (!LLDP_TYPE_VALID(type->type)) { + type->type = 0; + return EDPVS_INVAL; + } + + type->subtype = *(lldpdu + 2); + if (!LLDP_PORT_ID_VALID(type->subtype)) { + type->subtype = 0; + return EDPVS_INVAL; + } + + if (len) + *len = DPVS_LLDP_TL_LEN(*((uint16_t *)lldpdu)); + + return EDPVS_OK; +} + +static int lldp_local_pdu_port_id(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + size_t datalen = strlen(dev->name); + + assert(datalen < IFNAMSIZ); + + if (len >= 2 + 1 + datalen) { + *((uint16_t *)buf) = DPVS_LLDP_TL(LLDP_TYPE_PORT_ID, 1 + datalen); + buf[2] = LLDP_PORT_ID_INTERFACE_NAME; + rte_memcpy(&buf[3], &dev->name, datalen); + } else { + memset(buf, 0, len); + } + + return 2 + 1 + datalen; +} + +static int lldp_dump_port_id(const struct lldp_entry *e, char *buf, size_t len) +{ + const uint8_t *ptr = (const uint8_t *)e->value; /* Port ID Subtype */ + int pos = 0; + char tbuf[512], ipbuf[64]; + + lldp_dump_snprintf(tbuf, pos, "%s (%d)\n", "Port ID TLV", e->type.type); + assert(e->type.subtype == *ptr); + + ++ptr; /* Port ID Data */ + switch (e->type.subtype) { + case LLDP_PORT_ID_INTERFACE_ALIAS: + lldp_dump_snprintf(tbuf, pos, "%s", "\tInterface Alias: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_PORT_ID_PORT_COMPONENT: + lldp_dump_snprintf(tbuf, pos, "%s", "\tPort Component: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_PORT_ID_MAC_ADDRESS: + if (unlikely(e->len < 7)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tMAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); + break; + case LLDP_PORT_ID_NETWORK_ADDRESS: + switch (*ptr) { + case LLDP_ADDR_IPV4: + if (unlikely(e->len < 6)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv4: %s\n", inet_ntop(AF_INET, ptr + 1, + ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + case LLDP_ADDR_IPV6: + if (unlikely(e->len < 18)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv6: %s\n", inet_ntop(AF_INET6, ptr + 1, + ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + default: + if (unlikely(e->len <= 2)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tNetwork Address Type %d:", *ptr); + pos += binary2hexstr(ptr + 1, e->len - 2, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + } + break; + case LLDP_PORT_ID_INTERFACE_NAME: + lldp_dump_snprintf(tbuf, pos, "%s", "\tInterface Name: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len - 1, "\n"); + break; + case LLDP_PORT_ID_AGENT_CIRCUIT_ID: + lldp_dump_snprintf(tbuf, pos, "\t%s: ", "Agent Circuit ID"); + pos += binary2hexstr(ptr, e->len - 1, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + case LLDP_PORT_ID_LOCALLY_ASSIGNED: + lldp_dump_snprintf(tbuf, pos, "%s", "\tLocal: "); + lldp_dump_strcpy(tbuf, pos, ptr, e->len -1, "\n"); + break; + default: + lldp_dump_snprintf(tbuf, pos, "\t%s: ", "Bad Port ID"); + pos += binary2print(ptr, e->len - 1, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + } + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static int lldp_local_pdu_ttl(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + uint16_t *data; + + if (len >= 4) { + data = (uint16_t *)buf; + *data++ = DPVS_LLDP_TL(LLDP_TYPE_TTL, 2); + *data = rte_cpu_to_be_16(DPVS_LLDP_TTL_DEFAULT); + } else { + memset(buf, 0, len); + } + + return 4; +} + +static int lldp_dump_ttl(const struct lldp_entry *e, char *buf, size_t len) +{ + uint16_t *ttl = (uint16_t *)e->value; + return snprintf(buf, len, "Time to Live TLV (%d)\n\t%d\n", e->type.type, rte_be_to_cpu_16(*ttl)); +} + +static int lldp_on_change_ttl(const struct lldp_entry *e) +{ + struct lldp_port *port = e->port; + uint16_t ttl; + const void *ptr; + + /* Lifespan of local lldp caches is not decided by ttl. Actually, they are + * updated periodically in every DPVS_LLDP_UPDATE_INTERVAL second. If not updated + * in 3 * DPVS_LLDP_UPDATE_INTERVAL seconds, they are expired and removed. + * */ + if (port->neigh == DPVS_LLDP_NODE_LOCAL) + return EDPVS_OK; + + ptr = &e->value[0]; + ttl = rte_be_to_cpu_16(*((uint16_t *)ptr)); + if (ttl != port->timeout) { + RTE_LOG(INFO, LLDP, "%s: update neigh lldp ttl %u -> %u\n", __func__, port->timeout, ttl); + port->timeout = ttl; + } + + return EDPVS_OK; +} + +static int lldp_local_pdu_port_desc(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + size_t desc_len; + char desc[128]; + + desc_len = snprintf(desc, sizeof(desc), "DPVS Server Port: Interface %s, Index %d, Kni %s", + dev->name, dev->id, dev->kni.kni ? dev->kni.name : "None"); + if (2 + desc_len <= len) { + *((uint16_t *)buf) = DPVS_LLDP_TL(LLDP_TYPE_PORT_DESC, desc_len); + rte_memcpy(&buf[2], desc, desc_len); + } else { + memset(buf, 0, len); + } + + return 2 + desc_len; +} + +static int lldp_dump_port_desc(const struct lldp_entry *e, char *buf, size_t len) +{ + int pos = 0; + char tbuf[1024]; + + lldp_dump_snprintf(tbuf, pos, "Port Description TLV (%d)\n\t", e->type.type); + if (likely(e->len > 0)) + lldp_dump_strcpy(tbuf, pos, e->value, e->len, "\n"); + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + + return pos; +} + +static int lldp_local_pdu_sys_name(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + size_t host_len; + char hostname[HOST_NAME_MAX + 1]; + + if (unlikely(gethostname(hostname, sizeof(hostname)) != 0)) + snprintf(hostname, sizeof(hostname), "%s", "Unknown"); + + host_len = strlen(hostname); + if (2 + host_len <= len) { + *((uint16_t *)buf) = DPVS_LLDP_TL(LLDP_TYPE_SYS_NAME, host_len); + rte_memcpy(&buf[2], hostname, host_len); + } else { + memset(buf, 0, len); + } + + return 2 + host_len; +} + +static int lldp_dump_sys_name(const struct lldp_entry *e, char *buf, size_t len) +{ + int pos = 0; + char tbuf[1024]; + + lldp_dump_snprintf(tbuf, pos, "System Name TLV (%d)\n\t", e->type.type); + if (likely(e->len > 0)) + lldp_dump_strcpy(tbuf, pos, e->value, e->len, "\n"); + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + + return pos; +} + +static int lldp_local_pdu_sys_desc(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + int rc; + + rc = snprintf(buf + 2, len - 2, "%s %s %s %s %s, Serail Number %s", + lldp_uname.sysname, lldp_uname.nodename, lldp_uname.release, + lldp_uname.version, lldp_uname.machine, lldp_sn); + if (unlikely(rc < 0)) + return EDPVS_IO; + *((uint16_t *)buf) = DPVS_LLDP_TL(LLDP_TYPE_SYS_DESC, rc); + + return rc; +} + +static int lldp_dump_sys_desc(const struct lldp_entry *e, char *buf, size_t len) +{ + int pos = 0; + char tbuf[1024]; + + lldp_dump_snprintf(tbuf, pos, "System Description TLV (%d)\n\t", e->type.type); + if (likely(e->len > 0)) + lldp_dump_strcpy(tbuf, pos, e->value, e->len, "\n"); + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static const char *lldp_bit2sys_cap(uint16_t capacities, uint8_t bitpos) +{ + switch (capacities & (1UL << bitpos)) { + case 0x0001: + return "Other"; + case 0x0002: + return "Repeater"; + case 0x0004: + return "Bridge"; + case 0x0008: + return "WLAN Access Point"; + case 0x0010: + return "Router"; + case 0x0020: + return "Telephone"; + case 0x0040: + return "DOCSIS cable device"; + case 0x0080: + return "Station Only"; + case 0x0100: + return "Client"; + case 0x0200: + return "ISDN Terminal Adapter"; + case 0x0400: + return "Cryptographic Device"; + case 0x0800: + return "Voice Gateway"; + case 0x1000: + return "LAN Endpoint"; + case 0x2000: + case 0x4000: + case 0x8000: + return "Reserved"; + default: + return ""; + } + return ""; +} + +static int lldp_local_pdu_sys_cap(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + if (len >= 2 + 4) { + *((uint16_t *)&buf[0]) = DPVS_LLDP_TL(LLDP_TYPE_SYS_CAP, 4); + *((uint16_t *)&buf[2]) = rte_cpu_to_be_16(0x80); /* Capacity: Station Only */ + *((uint16_t *)&buf[4]) = rte_cpu_to_be_16(0x80); /* Enabled: Station Only */ + } + + return 2 + 4; +} + +static int lldp_dump_sys_cap(const struct lldp_entry *e, char *buf, size_t len) +{ + uint8_t i, first; + uint16_t capacities, enables; + int pos = 0; + char tbuf[256]; + const void *ptr; + + if (e->len != 4) + return EDPVS_INVPKT; + ptr = &e->value[0]; + capacities = rte_be_to_cpu_16(*((uint16_t *)ptr)); + ptr = &e->value[2]; + enables = rte_be_to_cpu_16(*((uint16_t *)ptr)); + + lldp_dump_snprintf(tbuf, pos, "System Capabilities TLV (%d)\n", e->type.type); + + first = 1; + for (i = 0; i < 16; i++) { + if (!(capacities & (1UL << i))) + continue; + if (first) { + lldp_dump_snprintf(tbuf, pos, "\tSystem capabilities: %s", + lldp_bit2sys_cap(capacities, i)); + first = 0; + } else { + lldp_dump_snprintf(tbuf, pos, ", %s", lldp_bit2sys_cap(capacities, i)); + } + } + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + + first = 1; + for (i = 0; i < 16; i++) { + if (!(enables & (1UL << i))) + continue; + if (first) { + lldp_dump_snprintf(tbuf, pos, "\tEnabled capabilities: %s", + lldp_bit2sys_cap(enables, i)); + first = 0; + } else { + lldp_dump_snprintf(tbuf, pos, ", %s", lldp_bit2sys_cap(enables, i)); + } + } + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 11] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static int lldp_parse_type_mng_addr(const char *lldpdu, lldp_type_t *type, uint16_t *len) +{ + assert(NULL != type); + + type->type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + if (!LLDP_TYPE_VALID(type->type)) { + type->type = 0; + return EDPVS_INVAL; + } + type->subtype = *((uint8_t *)(lldpdu + 3)); + + if (len) + *len = DPVS_LLDP_TL_LEN(*((uint16_t *)lldpdu)); + + return EDPVS_OK; +} + +static int lldp_local_pdu_mng_addr(const struct netif_port *dev, uint32_t subtype, char *buf, size_t len) +{ + int rc; + uint8_t tbuf[512]; + uint8_t *ptr; + struct sockaddr_storage addr; + char ifname[IFNAMSIZ]; + uint16_t typlen; + + ptr = tbuf + 2; + *(ptr + 1) = subtype; + switch (subtype) { + case LLDP_ADDR_ALL802: + *ptr = 7; + rte_memcpy(ptr + 2, &dev->addr, 6); + ptr += 8; + break; + case LLDP_ADDR_IPV4: + *ptr = 5; + rc = get_host_addr(dev->kni.kni ? dev->kni.name : NULL, &addr, NULL, ifname, NULL); + if (rc < 0) + return rc; + if (rc & 0x1) + rte_memcpy(ptr + 2, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, 4); + else + ifname[0] = '\0'; + ptr += 6; + break; + case LLDP_ADDR_IPV6: + *ptr = 17; + rc = get_host_addr(dev->kni.kni ? dev->kni.name : NULL, NULL, &addr, NULL, ifname); + if (rc < 0) + return rc; + if (rc &0x2) + rte_memcpy(ptr + 2, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16); + else + ifname[0] = '\0'; + ptr += 18; + break; + default: + return EDPVS_NOTSUPP; + } + + if (subtype == LLDP_ADDR_ALL802) { + *ptr++ = 2; /* Interface Subtype: Ifindex */ + *((uint32_t *)ptr) = rte_cpu_to_be_32(dev->id); + } else if (ifname[0]) { + *ptr++ = 2; /* Interface Subtype: Ifindex */ + rc = linux_ifname2index(ifname); + if (rc < 0) + return EDPVS_SYSCALL; + *((uint32_t *)ptr) = rte_cpu_to_be_32(rc); + } else { + *ptr++ = 1; /* Interface Subtype: Unknown */ + *((uint32_t *)ptr) = 0; + } + + ptr += 4; /* OID String Length */ + *ptr++ = 0; + + typlen = DPVS_LLDP_TL(LLDP_TYPE_MNG_ADDR, ptr - tbuf - 2); + rte_memcpy(tbuf, &typlen, 2); + + if (ptr - tbuf > len) + rte_memcpy(buf, tbuf, len); + else + rte_memcpy(buf, tbuf, ptr - tbuf); + return ptr - tbuf; +} + +static int lldp_dump_mng_addr(const struct lldp_entry *e, char *buf, size_t len) +{ + const uint8_t *ptr = (const uint8_t *)e->value; /* Address Length */ + uint8_t addrlen, intf_subtype, oidlen; + int pos = 0; + char tbuf[1024], ipbuf[64]; + + lldp_dump_snprintf(tbuf, pos, "%s (%d)\n", "Management Address TLV", e->type.type); + addrlen = *ptr; + ++ptr; /* Address Subtype */ + assert(e->type.subtype == *ptr); + + ++ptr; /* Management Address */ + switch (e->type.subtype) { + case LLDP_ADDR_ALL802: + if (unlikely(addrlen < 7)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tMAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); + break; + case LLDP_ADDR_IPV4: + if (unlikely(addrlen < 5)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv4: %s\n", + inet_ntop(AF_INET, ptr, ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + case LLDP_ADDR_IPV6: + if (unlikely(addrlen < 17)) + return EDPVS_INVPKT; + lldp_dump_snprintf(tbuf, pos, "\tIPv6: %s\n", + inet_ntop(AF_INET6, ptr, ipbuf, sizeof(ipbuf)) ?: "Unknown"); + break; + default: + lldp_dump_snprintf(tbuf, pos, "\tNetwork Address Type(%d): ", e->type.subtype); + pos += binary2hexstr(ptr, addrlen - 1, &tbuf[pos], sizeof(tbuf) - pos); + if (unlikely(pos >= sizeof(tbuf))) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + break; + } + + ptr = ptr + addrlen - 1; /* Interface Subtype */ + intf_subtype = *ptr; + switch (intf_subtype) { + case 1: + lldp_dump_snprintf(tbuf, pos, "\tUnknown interface subtype(%d): ", intf_subtype); + break; + case 2: + lldp_dump_snprintf(tbuf, pos, "%s", "\tIfindex: "); + break; + case 3: + lldp_dump_snprintf(tbuf, pos, "%s", "\tSystem port number: "); + break; + default: + lldp_dump_snprintf(tbuf, pos, "\tUnsupported interface subtype(%d): ", intf_subtype); + break; + } + ++ptr; /* Interface */ + lldp_dump_snprintf(tbuf, pos, "%d\n", rte_be_to_cpu_32(*((uint32_t *)ptr))); + + ptr += 4; /* OID String Length */ + oidlen = *ptr; + + ++ptr; /* OID String */ + if (oidlen > 128) + lldp_dump_snprintf(tbuf, pos, "\tOID: Invalid length = %d\n", oidlen); + else if (oidlen > 0) { + lldp_dump_snprintf(tbuf, pos, "%s", "\tOID: "); + pos += binary2hexstr((const uint8_t *)ptr, oidlen, &tbuf[pos], sizeof(tbuf) - pos); + if (pos >= sizeof(tbuf)) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + } + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static int lldp_parse_type_org(const char *lldpdu, lldp_type_t *type, uint16_t *len) +{ + assert(type != NULL); + + type->type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + if (!LLDP_TYPE_VALID(type->type)) { + type->type = 0; + return EDPVS_INVAL; + } + + /* subtype := ((24-bit Orgnization Unique Code) << 8) | (8-bit Subtype) */ + type->subtype = rte_be_to_cpu_32(*((uint32_t *)&lldpdu[2])); + + if (len) + *len = DPVS_LLDP_TL_LEN(*((uint16_t *)lldpdu)); + + return EDPVS_OK; +} + +static int lldp_dump_org_specific(const struct lldp_entry *e, char *buf, size_t len) +{ + // TODO: Implement Organizationally Specific TLVs + + const unsigned char *ptr = (unsigned char *)e->value; + int pos = 0; + char tbuf[1024]; + + if (e->len < 4) + return EDPVS_INVPKT; + + lldp_dump_snprintf(tbuf, pos, "Organizationally Specific TLV (%d): Code %02x:%02x:%02x, " + "Subtype %02d\n\t", e->type.type, ptr[0], ptr[1], ptr[2], ptr[3]); + pos += binary2hexstr((const uint8_t *)(&ptr[4]), e->len - 4, &tbuf[pos], sizeof(tbuf) - pos); + if (pos >= sizeof(tbuf)) + return EDPVS_NOROOM; + lldp_dump_snprintf(tbuf, pos, "%c", '\n'); + + if (pos >= len) { + rte_memcpy(buf, tbuf, len - 1); + buf[len - 1] = '\0'; + } else { + rte_memcpy(buf, tbuf, pos); + buf[pos] = '\0'; + } + return pos; +} + +static struct lldp_port *lldp_port_get(portid_t pid, uint16_t neigh) +{ + struct lldp_port *lp; + + if (unlikely(neigh >= DPVS_LLDP_NODE_MAX)) + return NULL; + + list_for_each_entry(lp, &lldp_ports[neigh], node) { + if (lp->dev->id == pid) { + assert(lp->neigh == neigh); + return lp; + } + } + return NULL; +} + +static void lldp_port_hash(struct lldp_port *port) +{ + struct lldp_port *entry, *next = NULL; + + assert(port->neigh < DPVS_LLDP_NODE_MAX); + + list_for_each_entry(entry, &lldp_ports[port->neigh], node) { + if (entry->dev->id >= port->dev->id) { + next = entry; + break; + } + } + + if (NULL != next) + list_add_tail(&port->node, &next->node); + else + list_add_tail(&port->node, &lldp_ports[port->neigh]); +} + +static inline void lldp_port_unhash(struct lldp_port *port) +{ + list_del_init(&port->node); +} + +static int lldp_entry_del(struct lldp_entry *entry); +static int lldp_port_del(struct lldp_port *port, bool in_timer) +{ + int err; + struct lldp_entry *entry, *next; + + lldp_port_unhash(port); + + list_for_each_entry_safe(entry, next, &port->head, node) { + err = lldp_entry_del(entry); + if (err != EDPVS_OK) + RTE_LOG(WARNING, LLDP, "%s: fail to del lldp %s entry, port %s type %d:%d error %s\n", + __func__, port->neigh ? "neigh" : "local", port->dev->name, + entry->type.type, entry->type.subtype, dpvs_strerror(err)); + } + assert(port->entries == 0); + + if (in_timer) + err = dpvs_timer_cancel_nolock(&port->timer, true); + else + err = dpvs_timer_cancel(&port->timer, true); + if (err != EDPVS_OK) + RTE_LOG(WARNING, LLDP, "%s: fail to cancel lldp port timer, port %s error %s\n", + __func__, port->dev->name, dpvs_strerror(err)); + + rte_free(port); + return EDPVS_OK; +} + +static int lldp_port_timeout(void *arg) +{ + struct lldp_port *port = arg; + + RTE_LOG(DEBUG, LLDP,"%s: %s lldp cache on %s expired\n", __func__, + port->neigh == DPVS_LLDP_NODE_LOCAL ? "local" : "neighbor", + port->dev->name); + + lldp_port_del(port, true); + return DTIMER_STOP; +} + +static int lldp_port_add(struct netif_port *dev, uint16_t neigh, uint16_t timeout, bool in_timer) +{ + int err; + struct lldp_port *lp; + struct timeval to = { .tv_sec = timeout }; + + if (neigh >= DPVS_LLDP_NODE_MAX) + return EDPVS_INVAL; + + if (lldp_port_get(dev->id, neigh)) + return EDPVS_EXIST; + + lp = rte_zmalloc("lldp_port", sizeof(*lp), RTE_CACHE_LINE_SIZE); + if (unlikely(!lp)) + return EDPVS_NOMEM; + + lp->dev = dev; + lp->neigh = neigh; + lp->timeout = timeout ?: DPVS_LLDP_TTL_DEFAULT; + INIT_LIST_HEAD(&lp->head); + + lldp_port_hash(lp); + + dpvs_time_rand_delay(&to, 1000000); + if (in_timer) + err = dpvs_timer_sched_nolock(&lp->timer, &to, lldp_port_timeout, lp, true); + else + err = dpvs_timer_sched(&lp->timer, &to, lldp_port_timeout, lp, true); + if (err != EDPVS_OK) { + lldp_port_unhash(lp); + rte_free(lp); + return err; + } + + return EDPVS_OK; +} + +static struct lldp_entry *lldp_entry_get(const struct lldp_port *port, lldp_type_t type) +{ + struct lldp_entry *e; + + if (unlikely(NULL == port)) + return NULL; + + list_for_each_entry(e, &port->head, node) { + if (lldp_type_equal(e->type, type)) + return e; + } + return NULL; +} + +static void lldp_entry_hash(struct lldp_entry *e, struct lldp_port *port) +{ + struct lldp_entry *entry, *next = NULL; + + /* put LLDP_TYPE_END node at tail */ + if (unlikely(!e->type.type)) { + list_add_tail(&e->node, &port->head); + ++port->entries; + return; + } + + list_for_each_entry(entry, &port->head, node) { + if (!entry->type.type || lldp_type_cmp(&entry->type, &e->type) >= 0) { + next = entry; + break; + } + } + + if (NULL != next) + list_add_tail(&e->node, &next->node); + else + list_add_tail(&e->node, &port->head); + ++port->entries; +} + +static inline void lldp_entry_unhash(struct lldp_entry *e) +{ + list_del_init(&e->node); + --e->port->entries; +} + +static int lldp_entry_del(struct lldp_entry *entry) +{ + lldp_entry_unhash(entry); + rte_free(entry); + return EDPVS_OK; +} + +static int lldp_entry_add(struct lldp_port *port, char *lldpdu) +{ + int err; + lldp_type_t type; + uint16_t len; + struct lldp_entry *entry; + struct lldp_type_ops *ops; + + type.type = DPVS_LLDP_TL_TYPE((uint16_t)(*lldpdu)); + ops = lldp_type_get(type); + if (!ops) + return EDPVS_NOTSUPP; + err = ops->parse_type(lldpdu, &type, &len); + if (EDPVS_OK != err) + return err; + assert(len <= DPVS_LLDP_PDU_MAX); + + entry = lldp_entry_get(port, type); + if (entry) { + /* do update */ + if (entry->len >= len) { + entry->len = len; + entry->stale = 0; + rte_memcpy(&entry->typelen, lldpdu, len + 2); + if (ops->on_change) + return ops->on_change(entry); + return EDPVS_OK; + } + lldp_entry_del(entry); + } + + entry = rte_zmalloc("lldp_entry", sizeof(struct lldp_entry) + len + 2, RTE_CACHE_LINE_SIZE); + if (unlikely(!entry)) + return EDPVS_NOMEM; + entry->type = type; + entry->len = len; + entry->port = port; + rte_memcpy(&entry->typelen, lldpdu, len + 2); + + lldp_entry_hash(entry, port); + + if (ops->on_change) + return ops->on_change(entry); + return EDPVS_OK; +} + +static int lldp_dump_pdu(const struct lldp_port *port, char *buf, size_t buflen) +{ + int rc; + size_t room; + char *ptr; + struct lldp_entry *e; + struct lldp_type_ops *ops; + + ptr = buf; + room = buflen; + list_for_each_entry(e, &port->head, node) { + if (room <= 0) + return EDPVS_NOROOM; + ops = lldp_type_get(e->type); + if (unlikely(!ops)) + return EDPVS_NOTSUPP; + if (ops->dump) { + rc = ops->dump(e, ptr, room); + if (unlikely(rc < 0)) + return rc; + if (unlikely(rc > room)) + return EDPVS_NOROOM; + ptr += rc; + room -= rc; + } + } + + return EDPVS_OK; +} + +static int lldp_pdu_local_update(struct netif_port *dev, bool in_timer) +{ + int i, rc; + struct lldp_port *port; + struct lldp_type_ops *ops; + char buf[DPVS_LLDP_PDU_MAX]; + + static lldp_type_t local_lldp_types[] = { + { LLDP_TYPE_CHASSIS_ID, LLDP_CHASSIS_ID_MAC_ADDRESS }, + { LLDP_TYPE_PORT_ID, LLDP_PORT_ID_INTERFACE_NAME }, + { LLDP_TYPE_TTL, 0 }, + { LLDP_TYPE_PORT_DESC, 0 }, + { LLDP_TYPE_SYS_NAME, 0 }, + { LLDP_TYPE_SYS_DESC, 0 }, + { LLDP_TYPE_SYS_CAP, 0 }, + { LLDP_TYPE_MNG_ADDR, 1 }, /* ipv4 */ + { LLDP_TYPE_MNG_ADDR, 2 }, /* ipv6 */ + { LLDP_TYPE_END, 0 }, + }; + + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_LOCAL); + if (!port) { + /* timeout of 3*DPVS_LLDP_UPDATE_INTERVA ensures local lldp caches persist */ + rc = lldp_port_add(dev, DPVS_LLDP_NODE_LOCAL, 3 * DPVS_LLDP_UPDATE_INTERVAL, in_timer); + if (unlikely(EDPVS_OK != rc)) + return rc; + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_LOCAL); + assert(port != NULL); + } + + for (i = 0; i < NELEMS(local_lldp_types); i++) { + ops = lldp_type_get(local_lldp_types[i]); + if (!ops || !ops->local_lldp) + continue; + rc = ops->local_lldp(dev, local_lldp_types[i].subtype, buf, sizeof(buf)); + if (unlikely(rc < 0)) { + RTE_LOG(INFO, LLDP, "%s: fail to generate local lldp pdu, type %d.%d," + " err %s\n", __func__, local_lldp_types[i].type, + local_lldp_types[i].subtype, dpvs_strerror(rc)); + continue; + } + if (unlikely(rc > sizeof(buf))) + return EDPVS_NOROOM; + rc = lldp_entry_add(port, buf); + if (EDPVS_OK != rc) + return rc; + } + + if (in_timer) + dpvs_timer_reset_nolock(&port->timer, true); + else + dpvs_timer_reset(&port->timer, true); + + return EDPVS_OK; +} + +static int lldp_pdu_neigh_update(struct netif_port *dev, const struct rte_mbuf *mbuf, bool in_timer) +{ + int err; + char *ptr; + size_t totlen; + uint16_t typelen; + uint16_t len; + uint8_t type; + bool check_stale = false; + struct lldp_port *port; + struct lldp_entry *entry, *next; + struct timeval timeout; + + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_NEIGH); + if (!port) { + err = lldp_port_add(dev, DPVS_LLDP_NODE_NEIGH, DPVS_LLDP_TTL_DEFAULT, in_timer); + if (unlikely(EDPVS_OK != err)) + return err; + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_NEIGH); + assert(port != NULL); + } else { + check_stale = true; + list_for_each_entry(entry, &port->head, node) + entry->stale = 1; + } + + totlen = mbuf->data_len; + ptr = rte_pktmbuf_mtod(mbuf, char *); + while (totlen > 0) { + typelen = *((uint16_t*)ptr); + type = DPVS_LLDP_TL_TYPE(typelen); + len = DPVS_LLDP_TL_LEN(typelen) + 2; + err = lldp_entry_add(port, ptr); + if (unlikely(EDPVS_OK != err && EDPVS_NOTSUPP != err)) + return err; + totlen -= len; + ptr += len; + if (LLDP_TYPE_END == type) + break; + } + + if (check_stale) { + list_for_each_entry_safe(entry, next, &port->head, node) { + if (entry->stale) + lldp_entry_del(entry); + } + } + + timeout.tv_sec = port->timeout; + dpvs_time_rand_delay(&timeout, 1000000); + if (in_timer) + err = dpvs_timer_update_nolock(&port->timer, &timeout, true); + else + err = dpvs_timer_update(&port->timer, &timeout, true); + return err; +} + +static int lldp_local_update_all(void *arg) +{ + int err; + portid_t i, start, end; + struct netif_port *dev; + + RTE_LOG(DEBUG, LLDP, "%s: updating local lldp cache\n", __func__); + + netif_physical_port_range(&start, &end); + for (i = start; i < end; i++) { + dev = netif_port_get(i); + assert(dev != NULL); + if (!(dev->flag & NETIF_PORT_FLAG_LLDP)) + continue; + err = lldp_pdu_local_update(dev, true); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: fail to update local lldp cache on port %s: %s\n", + __func__, dev->name, dpvs_strerror(err)); + } + + return DTIMER_OK; +} + +static int lldp_xmit(struct netif_port *dev, bool in_timer) +{ + int err; + char *ptr; + struct rte_mbuf *mbuf; + struct lldp_port *port; + struct lldp_entry *entry; + struct rte_ether_hdr *ehdr; + + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_LOCAL); + if (!port || port->entries <= 0) { + err = lldp_pdu_local_update(dev, in_timer); // FIXME: update lldp cache asynchronously + if (EDPVS_OK != err) { + RTE_LOG(ERR, LLDP, "%s: lldp_pdu_local_update failed: %s\n", + __func__, dpvs_strerror(err)); + return err; + } + port = lldp_port_get(dev->id, DPVS_LLDP_NODE_LOCAL); + if (unlikely(!port)) + return EDPVS_NOTEXIST; + if (port->entries <= 0) + return EDPVS_OK; + } + + mbuf = rte_pktmbuf_alloc(dev->mbuf_pool); + if (unlikely(!mbuf)) + return EDPVS_NOMEM; + mbuf_userdata_reset(mbuf); + + list_for_each_entry(entry, &port->head, node) { + ptr = rte_pktmbuf_append(mbuf, entry->len + 2); + if (unlikely(!ptr)) + return EDPVS_NOROOM; + rte_memcpy(ptr, &entry->typelen, entry->len + 2); + } + + ehdr = (struct rte_ether_hdr *)rte_pktmbuf_prepend(mbuf, sizeof(*ehdr)); + if (unlikely(!ptr)) + return EDPVS_NOROOM; + rte_memcpy(&ehdr->d_addr, &LLDP_ETHER_ADDR_DST, sizeof(ehdr->d_addr)); + rte_memcpy(&ehdr->s_addr, &dev->addr, sizeof(ehdr->s_addr)); + ehdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_LLDP); + + if (dev->type == PORT_TYPE_BOND_SLAVE) { + // FIXME: + // How to send LLDP packet on a specified slave port? I found no solutions to it via + // DPDK API. Maybe changes should be made to bond PMD driver to solve the problem. + // So I save the slave port id in mbuf, and hope bond PMD driver may consider it when + // distributing mbufs to slave ports. + // + // Store the slave port id into mbuf->port? + // No! mbuf->port is reset to the bond master's port id in the forthcoming transmit process. + // Use mbuf->hash.txadapter.reserved2 instead. Hope no conflictions. Remember to reset it to + // RTE_MBUF_PORT_INVALID in rte_pktmbuf_alloc. + // + mbuf->hash.txadapter.reserved2 = dev->id; + //MBUF_USERDATA(mbuf, portid_t, MBUF_FIELD_ORIGIN_PORT) = port->id; + dev = dev->bond->slave.master; + } + + return netif_xmit(mbuf, dev); + +} + +static int lldp_xmit_all(void *arg) +{ + int err; + portid_t i, start, end; + struct netif_port *dev; + + netif_physical_port_range(&start, &end); + for (i = start; i < end; i++) { + dev = netif_port_get(i); + assert(dev != NULL); + if (!(dev->flag & NETIF_PORT_FLAG_LLDP)) + continue; + err = lldp_xmit(dev, true); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: fail to xmit lldp frame on port %s: %s\n", + __func__, dev->name, dpvs_strerror(err)); + } + + return DTIMER_OK; +} + +static int lldp_ether_addr_filter(bool add) +{ + int err; + portid_t i, start, end; + struct netif_port *dev; + + netif_physical_port_range(&start, &end); + for (i = start; i < end; i++) { + dev = netif_port_get(i); + assert(dev != NULL); + if (add) + err = netif_mc_add(dev, &LLDP_ETHER_ADDR_DST); + else + err = netif_mc_del(dev, &LLDP_ETHER_ADDR_DST); + if (err != EDPVS_OK) + return err; + } + + return EDPVS_OK; +} + +static int lldp_xmit_start(void) +{ + int err; + struct timeval timeout1 = { .tv_sec = DPVS_LLDP_TX_INTERVAL }; + struct timeval timeout2 = { .tv_sec = DPVS_LLDP_UPDATE_INTERVAL }; + + assert(rte_lcore_id() == rte_get_main_lcore()); + + err = lldp_ether_addr_filter(true); + if (EDPVS_OK != err && EDPVS_EXIST != err) { + RTE_LOG(WARNING, LLDP, "%s: failed to add lldp multicast ether address -- %s\n", + __func__, dpvs_strerror(err)); + return err; + } + + dpvs_time_rand_delay(&timeout1, 1000000); + err = dpvs_timer_sched_period(&lldp_xmit_timer, &timeout1, lldp_xmit_all, NULL, true); + if (EDPVS_OK != err) { + RTE_LOG(WARNING, LLDP, "%s: failed to schedule lldp_xmit_timer -- %s\n", + __func__, dpvs_strerror(err)); + lldp_ether_addr_filter(false); + return err; + } + + dpvs_time_rand_delay(&timeout2, 1000000); + err = dpvs_timer_sched_period(&lldp_update_timer, &timeout2, lldp_local_update_all, NULL, true); + if (EDPVS_OK != err) { + RTE_LOG(WARNING, LLDP, "%s: failed to schedule lldp_update_timer -- %s\n", + __func__, dpvs_strerror(err)); + dpvs_timer_cancel(&lldp_xmit_timer, true); + lldp_ether_addr_filter(false); + return err; + } + + return EDPVS_OK; +} + +static int lldp_xmit_stop(void) +{ + int err; + + assert(rte_lcore_id() == rte_get_main_lcore()); + + err = lldp_ether_addr_filter(false); + if (EDPVS_OK != err && EDPVS_NOTEXIST != err) { + RTE_LOG(WARNING, LLDP, "%s: failed to del lldp multicast ether address -- %s\n", + __func__, dpvs_strerror(err)); + return err; + } + + err = dpvs_timer_cancel(&lldp_xmit_timer, true); + if (EDPVS_OK != err) { + RTE_LOG(ERR, LLDP, "%s: failed to cancel lldp_xmit_timer -- %s\n", + __func__, dpvs_strerror(err)); + return err; + } + + err = dpvs_timer_cancel(&lldp_update_timer, true); + if (EDPVS_OK != err) { + RTE_LOG(ERR, LLDP, "%s: failed to cancel lldp_update_timer -- %s\n", + __func__, dpvs_strerror(err)); + return err; + } + + return EDPVS_OK; +} + +static int lldp_rcv(struct rte_mbuf *mbuf, struct netif_port *dev) +{ + int err; + portid_t pid; + static uint32_t seq = 0; + struct dpvs_msg *msg; + + if (!lldp_enable) + return EDPVS_KNICONTINUE; + + if (is_bond_port(dev->id)) { + pid = MBUF_USERDATA(mbuf, portid_t, MBUF_FIELD_ORIGIN_PORT); + dev = netif_port_get(pid); + if (unlikely(NULL == dev)) { + RTE_LOG(WARNING, LLDP, "%s: fail to find lldp physical device of port id %d\n", + __func__, pid); + rte_pktmbuf_free(mbuf); + return EDPVS_RESOURCE; + } + } + if (!(dev->flag & NETIF_PORT_FLAG_LLDP)) + return EDPVS_KNICONTINUE; + + /* redirect lldp mbuf to master lcore */ + msg = msg_make(MSG_TYPE_LLDP_RECV, seq++, DPVS_MSG_UNICAST, + rte_lcore_id(), sizeof(void *), &mbuf); + if (unlikely(NULL == msg)) { + rte_pktmbuf_free(mbuf); + return EDPVS_NOMEM; + } + + err = msg_send(msg, rte_get_main_lcore(), DPVS_MSG_F_ASYNC, NULL); + if (unlikely(EDPVS_OK != err)) { + RTE_LOG(WARNING, LLDP, "%s: fail to send mbuf to master lcore!\n", __func__); + rte_pktmbuf_free(mbuf); + } + msg_destroy(&msg); + return err; +} + +static int lldp_rcv_msg_cb(struct dpvs_msg *msg) +{ + int err; + portid_t pid, start, end; + struct netif_port *dev; + struct rte_mbuf *mbuf; + void *msgdata = msg->data; + + mbuf = *(struct rte_mbuf **)msgdata; + + pid = mbuf->port; + netif_bond_port_range(&start, &end); + if (pid < end && pid >= start) + pid = MBUF_USERDATA(mbuf, portid_t, MBUF_FIELD_ORIGIN_PORT); + + dev = netif_port_get(pid); + if (unlikely(NULL == dev)) { + RTE_LOG(WARNING, LLDP, "%s: fail to find lldp physical device of port id %d\n", + __func__, pid); + rte_pktmbuf_free(mbuf); + return EDPVS_RESOURCE; + } + + err = lldp_pdu_neigh_update(dev, mbuf, false); + rte_pktmbuf_free(mbuf); /* always consume the mbuf */ + return err; +} + +static int lldp_rcv_msg_register(void) +{ + lcoreid_t master_cid = rte_get_main_lcore(); + struct dpvs_msg_type mt = { + .type = MSG_TYPE_LLDP_RECV, + .mode = DPVS_MSG_UNICAST, + .prio = MSG_PRIO_LOW, + .cid = master_cid, + .unicast_msg_cb = lldp_rcv_msg_cb, + }; + + return msg_type_register(&mt); +} + +static int lldp_rcv_msg_unregister(void) +{ + lcoreid_t master_cid = rte_get_main_lcore(); + struct dpvs_msg_type mt = { + .type = MSG_TYPE_LLDP_RECV, + .mode = DPVS_MSG_UNICAST, + .prio = MSG_PRIO_LOW, + .cid = master_cid, + .unicast_msg_cb = lldp_rcv_msg_cb, + }; + + return msg_type_unregister(&mt); +} + +static int lldp_sockopt_set(sockoptid_t opt, const void *conf, size_t size) +{ + // TODO + return EDPVS_NOTSUPP; +} + +static int lldp_sockopt_get(sockoptid_t opt, const void *conf, size_t size, + void **out, size_t *outsize) +{ + const struct lldp_param *param = conf; + struct lldp_message *message; + struct netif_port *dev; + struct lldp_port *port; + int err; + + *outsize = 0; + *out = NULL; + + if (!conf || size < sizeof(*param) || !out || !outsize) + return EDPVS_INVAL; + + if (opt != SOCKOPT_GET_LLDP_SHOW) + return EDPVS_NOTSUPP; + + dev = netif_port_get_by_name(param->ifname); + if (!dev) { + RTE_LOG(WARNING, LLDP, "%s: no such device\n", __func__); + return EDPVS_NODEV; + } + + if (param->node >= DPVS_LLDP_NODE_MAX) { + RTE_LOG(WARNING, LLDP, "%s: invalid node type %d, only supports type " + "local(%d) and neigh(%d)\n", __func__, param->node, + DPVS_LLDP_NODE_LOCAL, DPVS_LLDP_NODE_NEIGH); + return EDPVS_INVAL; + } + + port = lldp_port_get(dev->id, param->node); + if (!port) { + RTE_LOG(INFO, LLDP, "%s: %s lldp port on %s not found!\n", __func__, + param->node == DPVS_LLDP_NODE_NEIGH ? "neighbor" : "local", dev->name); + return EDPVS_NOTEXIST; + } + + message = rte_calloc(NULL, 1, sizeof(*message), 0); + if (!message) + return EDPVS_NOMEM; + rte_memcpy(&message->param, param, sizeof(*param)); + err = lldp_dump_pdu(port, message->message, sizeof(message->message)); + if (EDPVS_OK != err) { + RTE_LOG(WARNING, LLDP, "%s: lldp_dump_pdu failed -- %s\n", + __func__, dpvs_strerror(err)); + rte_free(message); + return err; + } + + *out = message; + *outsize = sizeof(*message); + return EDPVS_OK; +} + +static struct dpvs_sockopts lldp_sockopts = { + .version = SOCKOPT_VERSION, + .set_opt_min = SOCKOPT_SET_LLDP_TODO, + .set_opt_max = SOCKOPT_SET_LLDP_TODO, + .set = lldp_sockopt_set, + .get_opt_min = SOCKOPT_GET_LLDP_SHOW, + .get_opt_max = SOCKOPT_GET_LLDP_SHOW, + .get = lldp_sockopt_get, +}; + +static struct lldp_type_ops lldp_ops[] = { + { + .type = LLDP_TYPE_END, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_end, + .dump = lldp_dump_end, + }, + { + .type = LLDP_TYPE_CHASSIS_ID, + .parse_type = lldp_parse_type_chassis_id, + .local_lldp = lldp_local_pdu_chassis_id, + .dump = lldp_dump_chassis_id, + }, + { + .type = LLDP_TYPE_PORT_ID, + .parse_type = lldp_parse_type_port_id, + .local_lldp = lldp_local_pdu_port_id, + .dump = lldp_dump_port_id, + }, + { + .type = LLDP_TYPE_TTL, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_ttl, + .dump = lldp_dump_ttl, + .on_change = lldp_on_change_ttl, + }, + { + .type = LLDP_TYPE_PORT_DESC, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_port_desc, + .dump = lldp_dump_port_desc, + }, + { + .type = LLDP_TYPE_SYS_NAME, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_sys_name, + .dump = lldp_dump_sys_name, + }, + { + .type = LLDP_TYPE_SYS_DESC, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_sys_desc, + .dump = lldp_dump_sys_desc, + }, + { + .type = LLDP_TYPE_SYS_CAP, + .parse_type = lldp_parse_type_default, + .local_lldp = lldp_local_pdu_sys_cap, + .dump = lldp_dump_sys_cap, + }, + { + .type = LLDP_TYPE_MNG_ADDR, + .parse_type = lldp_parse_type_mng_addr, + .local_lldp = lldp_local_pdu_mng_addr, + .dump = lldp_dump_mng_addr, + }, + { + .type = LLDP_TYPE_ORG, + .parse_type = lldp_parse_type_org, + .local_lldp = NULL, + .dump = lldp_dump_org_specific, + } +}; + +static struct pkt_type dpvs_lldp_pkt_type = { + //.type = rte_cpu_to_be_16(RTE_ETHER_TYPE_LLDP), + .func = lldp_rcv, + .port = NULL, +}; + +int dpvs_lldp_init(void) +{ + int i, err; + + lldp_serail_number_init(); + + if (unlikely(uname(&lldp_uname) < 0)) + return EDPVS_SYSCALL; + + for (i = 0; i < DPVS_LLDP_NODE_MAX; i++) + INIT_LIST_HEAD(&lldp_ports[i]); + + for (i = 0; i < NELEMS(lldp_ops); i++) { + err = lldp_type_register(&lldp_ops[i]); + assert(EDPVS_OK == err); + } + + err = lldp_rcv_msg_register(); + if (EDPVS_OK != err) + goto unreg_lldp_ops; + + err = sockopt_register(&lldp_sockopts); + if (EDPVS_OK != err) + goto unreg_msg; + + dpvs_lldp_pkt_type.type = rte_cpu_to_be_16(RTE_ETHER_TYPE_LLDP); + err = netif_register_pkt(&dpvs_lldp_pkt_type); + if (EDPVS_OK != err) + goto unreg_sockopt; + + if (lldp_enable) { + err = lldp_xmit_start(); + if (EDPVS_OK != err) + goto unreg_pkttype; + } + + return EDPVS_OK; + +unreg_pkttype: + netif_unregister_pkt(&dpvs_lldp_pkt_type); +unreg_sockopt: + sockopt_unregister(&lldp_sockopts); +unreg_msg: + lldp_rcv_msg_unregister(); +unreg_lldp_ops: + for (i = 0; i < NELEMS(lldp_ops); i++) + lldp_type_unregister(&lldp_ops[i]); + return err; +} + +int dpvs_lldp_term(void) +{ + int i, err; + + if (lldp_enable) + lldp_xmit_stop(); + + dpvs_lldp_pkt_type.type = rte_cpu_to_be_16(RTE_ETHER_TYPE_LLDP); + err = netif_unregister_pkt(&dpvs_lldp_pkt_type); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: fail to unregister lldp packet type\n", __func__); + + err = sockopt_unregister(&lldp_sockopts); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: fail to unregister lldp msg\n", __func__); + err = lldp_rcv_msg_unregister(); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: fail to unregister lldp msg\n", __func__); + + for (i = 0; i < NELEMS(lldp_ops); i++) { + err = lldp_type_unregister(&lldp_ops[i]); + if (EDPVS_OK != err) + RTE_LOG(WARNING, LLDP, "%s: lldp_type_unregister(%d) failed\n", __func__, i); + } + + return EDPVS_OK; +} diff --git a/src/main.c b/src/main.c index 00ca89655..5cf291a24 100644 --- a/src/main.c +++ b/src/main.c @@ -311,6 +311,8 @@ int main(int argc, char *argv[]) gettimeofday(&tv, NULL); srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); + srand48(tv.tv_sec ^ tv.tv_usec ^ getpid()); + rte_srand((uint64_t)(tv.tv_sec ^ tv.tv_usec ^ getpid())); sys_start_time(); if (get_numa_nodes() > DPVS_MAX_SOCKET) { diff --git a/src/mbuf.c b/src/mbuf.c index 7d10d54d3..8c913eb36 100644 --- a/src/mbuf.c +++ b/src/mbuf.c @@ -209,6 +209,11 @@ int mbuf_init(void) .size = sizeof(mbuf_userdata_field_route_t), .align = 8, }, + [ MBUF_FIELD_ORIGIN_PORT ] = { + .name = "origin_port", + .size = sizeof(portid_t), + .align = 2, + }, }; for (i = 0; i < NELEMS(rte_mbuf_userdata_fields); i++) { diff --git a/src/netif.c b/src/netif.c index d2ae6cf91..0a071a1dc 100644 --- a/src/netif.c +++ b/src/netif.c @@ -28,6 +28,7 @@ #include "conf/common.h" #include "netif.h" #include "netif_addr.h" +#include "conf/netif_addr.h" #include "vlan.h" #include "ctrl.h" #include "list.h" @@ -160,16 +161,32 @@ static struct list_head port_ntab[NETIF_PORT_TABLE_BUCKETS]; /* hashed by name * /* function declarations */ static void kni_lcore_loop(void *dummy); -static inline bool is_physical_port(portid_t pid) +bool is_physical_port(portid_t pid) { return pid >= phy_pid_base && pid < phy_pid_end; } -static inline bool is_bond_port(portid_t pid) +bool is_bond_port(portid_t pid) { return pid >= bond_pid_base && pid < bond_pid_end; } +void netif_physical_port_range(portid_t *start, portid_t *end) +{ + if (start) + *start = phy_pid_base; + if (end) + *end = phy_pid_end; +} + +void netif_bond_port_range(portid_t *start, portid_t *end) +{ + if (start) + *start = bond_pid_base; + if (end) + *end = bond_pid_end; +} + bool is_lcore_id_valid(lcoreid_t cid) { if (unlikely(cid >= DPVS_MAX_LCORE)) @@ -2535,6 +2552,10 @@ void lcore_process_packets(struct rte_mbuf **mbufs, lcoreid_t cid, uint16_t coun lcore_stats[cid].dropped++; continue; } + + /* some protocols like LLDP may still like the originated port */ + MBUF_USERDATA(mbuf, portid_t, MBUF_FIELD_ORIGIN_PORT) = mbuf->port; + if (dev->type == PORT_TYPE_BOND_SLAVE) { dev = dev->bond->slave.master; mbuf->port = dev->id; @@ -2745,8 +2766,6 @@ static inline void netif_lcore_cleanup(void) } } -/********************************************** kni *************************************************/ - /* always update bond port macaddr and its KNI macaddr together */ static int update_bond_macaddr(struct netif_port *port) { @@ -2778,6 +2797,8 @@ static inline void free_mbufs(struct rte_mbuf **pkts, unsigned num) } } +/********************************************** kni *************************************************/ + void kni_ingress(struct rte_mbuf *mbuf, struct netif_port *dev) { if (!kni_dev_exist(dev)) @@ -3203,7 +3224,7 @@ portid_t netif_port_count(void) return port_id_end; } -struct netif_port *netif_alloc(size_t priv_size, const char *namefmt, +struct netif_port *netif_alloc(portid_t id, size_t priv_size, const char *namefmt, unsigned int nrxq, unsigned int ntxq, void (*setup)(struct netif_port *)) { @@ -3226,13 +3247,17 @@ struct netif_port *netif_alloc(size_t priv_size, const char *namefmt, return NULL; } - dev->id = netif_port_id_alloc(); + if (id != NETIF_PORT_ID_INVALID && !netif_port_get(id)) + dev->id = id; + else + dev->id = netif_port_id_alloc(); if (strstr(namefmt, "%d")) snprintf(dev->name, sizeof(dev->name), namefmt, dev->id); else snprintf(dev->name, sizeof(dev->name), "%s", namefmt); + rte_rwlock_init(&dev->dev_lock); dev->socket = SOCKET_ID_ANY; dev->hw_header_len = sizeof(struct rte_ether_hdr); /* default */ @@ -3256,7 +3281,6 @@ struct netif_port *netif_alloc(size_t priv_size, const char *namefmt, if (dev->mtu == 0) dev->mtu = ETH_DATA_LEN; - rte_rwlock_init(&dev->dev_lock); netif_mc_init(dev); dev->in_ptr = rte_zmalloc(NULL, sizeof(struct inet_device), RTE_CACHE_LINE_SIZE); @@ -3298,7 +3322,7 @@ static int bond_set_mc_list(struct netif_port *dev) slave = dev->bond->master.slaves[i]; rte_rwlock_write_lock(&slave->dev_lock); - err = __netif_mc_sync_multiple(slave, dev); + err = __netif_mc_sync_multiple(slave, dev, dev->bond->master.slave_nb); rte_rwlock_write_unlock(&slave->dev_lock); if (err != EDPVS_OK) { @@ -3320,10 +3344,11 @@ static int dpdk_set_mc_list(struct netif_port *dev) if (rte_eth_allmulticast_get(dev->id) == 1) return EDPVS_OK; - err = __netif_mc_dump(dev, addrs, &naddr); + err = __netif_mc_dump(dev, 0, addrs, &naddr); if (err != EDPVS_OK) return err; + RTE_LOG(DEBUG, NETIF, "%s: configuring %lu multicast hw-addrs\n", dev->name, naddr); err = rte_eth_dev_set_mc_addr_list(dev->id, addrs, naddr); if (err) { RTE_LOG(WARNING, NETIF, "%s: rte_eth_dev_set_mc_addr_list failed -- %s," @@ -3346,7 +3371,7 @@ static int netif_op_get_xstats(struct netif_port *dev, netif_nic_xstats_get_t ** if (nentries < 0) return EDPVS_DPDKAPIFAIL; - get = rte_calloc("xstats_get", 1, nentries * sizeof(struct netif_nic_xstats_entry), 0); + get = rte_calloc("xstats_get", 1, sizeof(*get) + nentries * sizeof(struct netif_nic_xstats_entry), 0); if (unlikely(!get)) return EDPVS_NOMEM; xstats = rte_calloc("xstats", 1, nentries * sizeof(struct rte_eth_xstat), 0); @@ -3446,78 +3471,10 @@ static inline void setup_dev_of_flags(struct netif_port *port) } if (port->dev_info.rx_offload_capa & DEV_RX_OFFLOAD_IPV4_CKSUM) port->flag |= NETIF_PORT_FLAG_RX_IP_CSUM_OFFLOAD; -} - -/* TODO: refactor it with netif_alloc */ -static struct netif_port* netif_rte_port_alloc(portid_t id, int nrxq, - int ntxq, const struct rte_eth_conf *conf) -{ - int ii; - struct netif_port *port; - - port = rte_zmalloc("port", sizeof(struct netif_port) + - sizeof(union netif_bond), RTE_CACHE_LINE_SIZE); - if (!port) { - RTE_LOG(ERR, NETIF, "%s: no memory\n", __func__); - return NULL; - } - - port->id = id; - port->bond = (union netif_bond *)(port + 1); - if (is_physical_port(id)) { - port->type = PORT_TYPE_GENERAL; /* update later in netif_rte_port_alloc */ - port->netif_ops = &dpdk_netif_ops; - } else if (is_bond_port(id)) { - port->type = PORT_TYPE_BOND_MASTER; - port->netif_ops = &bond_netif_ops; - } else { - RTE_LOG(ERR, NETIF, "%s: invalid port id: %d\n", __func__, id); - rte_free(port); - return NULL; - } - - if (port_name_alloc(id, port->name, sizeof(port->name)) != EDPVS_OK) { - RTE_LOG(ERR, NETIF, "%s: fail to get port name for port%d\n", - __func__, id); - rte_free(port); - return NULL; - } - - port->nrxq = nrxq; // update after port_rx_queues_get(); - port->ntxq = ntxq; // update after port_tx_queues_get(); - port->socket = rte_eth_dev_socket_id(id); - port->hw_header_len = sizeof(struct rte_ether_hdr); - if (port->socket == SOCKET_ID_ANY) - port->socket = rte_socket_id(); - port->mbuf_pool = pktmbuf_pool[port->socket]; - rte_eth_macaddr_get((uint8_t)id, &port->addr); // bonding mac is zero here - rte_eth_dev_get_mtu((uint8_t)id, &port->mtu); - rte_eth_dev_info_get((uint8_t)id, &port->dev_info); - port->dev_conf = *conf; - rte_rwlock_init(&port->dev_lock); - netif_mc_init(port); - - setup_dev_of_flags(port); - - port->in_ptr = rte_zmalloc(NULL, sizeof(struct inet_device), RTE_CACHE_LINE_SIZE); - if (!port->in_ptr) { - RTE_LOG(ERR, NETIF, "%s: no memory\n", __func__); - rte_free(port); - return NULL; - } - port->in_ptr->dev = port; - for (ii = 0; ii < DPVS_MAX_LCORE; ii++) { - INIT_LIST_HEAD(&port->in_ptr->ifa_list[ii]); - INIT_LIST_HEAD(&port->in_ptr->ifm_list[ii]); - } - - if (tc_init_dev(port) != EDPVS_OK) { - RTE_LOG(ERR, NETIF, "%s: fail to init TC\n", __func__); - rte_free(port); - return NULL; - } - return port; + /* enable lldp on physical port */ + if (is_physical_port(port->id)) + port->flag |= NETIF_PORT_FLAG_LLDP; } struct netif_port* netif_port_get(portid_t id) @@ -3916,7 +3873,6 @@ static int config_fdir_conf(struct rte_fdir_conf *fdir_conf) int netif_port_start(struct netif_port *port) { int ii, ret; - lcoreid_t cid; queueid_t qid; char promisc_on, allmulticast; char buf[512]; @@ -4057,14 +4013,11 @@ int netif_port_start(struct netif_port *port) if (port->netif_ops->op_update_addr) port->netif_ops->op_update_addr(port); - /* add in6_addr multicast address */ - rte_eal_mp_remote_launch(idev_add_mcast_init, port, CALL_MAIN); - RTE_LCORE_FOREACH_WORKER(cid) { - if ((ret = rte_eal_wait_lcore(cid)) < 0) { - RTE_LOG(WARNING, NETIF, "%s: lcore %d: multicast address add failed for device %s\n", - __func__, cid, port->name); - return ret; - } + /* ipv6 default addresses initialization */ + if ((ret = idev_addr_init(port->in_ptr)) != EDPVS_OK) { + RTE_LOG(WARNING, NETIF, "%s: idev_addr_init failed -- %d(%s)\n", + __func__, ret, dpvs_strerror(ret)); + return ret; } /* update rss reta */ @@ -4096,30 +4049,11 @@ int netif_port_stop(struct netif_port *port) return EDPVS_OK; } -int __netif_set_mc_list(struct netif_port *dev) -{ - if (!dev->netif_ops->op_set_mc_list) - return EDPVS_NOTSUPP; - - return dev->netif_ops->op_set_mc_list(dev); -} - -int netif_set_mc_list(struct netif_port *dev) -{ - int err; - - rte_rwlock_write_lock(&dev->dev_lock); - err = __netif_set_mc_list(dev); - rte_rwlock_write_unlock(&dev->dev_lock); - - return err; -} - int netif_port_register(struct netif_port *port) { struct netif_port *cur; int hash, nhash; - int err = EDPVS_OK; + int err; if (unlikely(NULL == port)) return EDPVS_INVAL; @@ -4142,10 +4076,15 @@ int netif_port_register(struct netif_port *port) list_add_tail(&port->nlist, &port_ntab[nhash]); g_nports++; - if (port->netif_ops->op_init) + if (port->netif_ops->op_init) { err = port->netif_ops->op_init(port); + if (err != EDPVS_OK) { + netif_port_unregister(port); + return err; + } + } - return err; + return EDPVS_OK; } int netif_port_unregister(struct netif_port *port) @@ -4225,6 +4164,7 @@ static int relate_bonding_device(void) } sport->type = PORT_TYPE_BOND_SLAVE; sport->bond->slave.master = mport; + sport->in_ptr->flags |= IDEV_F_NO_ROUTE; } mport->bond->master.slave_nb = i; } @@ -4332,14 +4272,42 @@ static char *find_conf_kni_name(portid_t id) return NULL; } +static void dpdk_port_setup(struct netif_port *dev) +{ + dev->type = PORT_TYPE_GENERAL; + dev->netif_ops = &dpdk_netif_ops; + dev->socket = rte_eth_dev_socket_id(dev->id); + dev->dev_conf = default_port_conf; + dev->bond = (union netif_bond *)(dev + 1); + + rte_eth_macaddr_get(dev->id, &dev->addr); + rte_eth_dev_get_mtu(dev->id, &dev->mtu); + rte_eth_dev_info_get(dev->id, &dev->dev_info); + setup_dev_of_flags(dev); +} + +static void bond_port_setup(struct netif_port *dev) +{ + dev->type = PORT_TYPE_BOND_MASTER; + dev->netif_ops = &bond_netif_ops; + dev->socket = rte_eth_dev_socket_id(dev->id); + dev->dev_conf = default_port_conf; + dev->bond = (union netif_bond *)(dev + 1); + + rte_eth_macaddr_get(dev->id, &dev->addr); + rte_eth_dev_get_mtu(dev->id, &dev->mtu); + rte_eth_dev_info_get(dev->id, &dev->dev_info); + setup_dev_of_flags(dev); +} + /* Allocate and register all DPDK ports available */ static void netif_port_init(void) { int nports, nports_cfg; portid_t pid; struct netif_port *port; - struct rte_eth_conf this_eth_conf; char *kni_name; + char ifname[IFNAMSIZ]; nports = dpvs_rte_eth_dev_count(); if (nports <= 0) @@ -4354,17 +4322,23 @@ static void netif_port_init(void) port_tab_init(); port_ntab_init(); - this_eth_conf = default_port_conf; - kni_init(); for (pid = 0; pid < nports; pid++) { + if (port_name_alloc(pid, ifname, sizeof(ifname)) != EDPVS_OK) + rte_exit(EXIT_FAILURE, "Port name allocation failed, exiting...\n"); + /* queue number will be filled on device start */ - port = netif_rte_port_alloc(pid, 0, 0, &this_eth_conf); + port = NULL; + if (is_physical_port(pid)) + port = netif_alloc(pid, sizeof(union netif_bond), ifname, 0, 0, dpdk_port_setup); + else if (is_bond_port(pid)) + port = netif_alloc(pid, sizeof(union netif_bond), ifname, 0, 0, bond_port_setup); if (!port) - rte_exit(EXIT_FAILURE, "Port allocate fail, exiting...\n"); + rte_exit(EXIT_FAILURE, "Port allocation failed, exiting...\n"); + if (netif_port_register(port) < 0) - rte_exit(EXIT_FAILURE, "Port register fail, exiting...\n"); + rte_exit(EXIT_FAILURE, "Port registration failed, exiting...\n"); } if (relate_bonding_device() < 0) @@ -4486,7 +4460,7 @@ int netif_vdevs_add(void) __func__, bond_cfg->name, bond_cfg->mode, bond_cfg->numa_node); return EDPVS_CALLBACKFAIL; } - bond_cfg->port_id = ret; /* relate port_id with port_name, used by netif_rte_port_alloc */ + bond_cfg->port_id = ret; /* relate port_id with port_name, used by port_name_alloc */ RTE_LOG(INFO, NETIF, "created bondig device %s: mode=%d, primary=%s, numa_node=%d\n", bond_cfg->name, bond_cfg->mode, bond_cfg->primary, bond_cfg->numa_node); @@ -4852,6 +4826,8 @@ static int get_port_basic(struct netif_port *port, void **out, size_t *out_len) get->ol_tx_tcp_csum = 1; if (port->flag & NETIF_PORT_FLAG_TX_UDP_CSUM_OFFLOAD) get->ol_tx_udp_csum = 1; + if (port->flag & NETIF_PORT_FLAG_LLDP) + get->lldp = 1; *out = get; *out_len = sizeof(netif_nic_basic_get_t); @@ -5201,6 +5177,15 @@ static int netif_sockopt_get(sockoptid_t opt, const void *in, size_t inlen, return EDPVS_NOTEXIST; ret = get_bond_status(port, out, outlen); break; + case SOCKOPT_NETIF_GET_MADDR: + if (!in) + return EDPVS_INVAL; + name = (char *)in; + port = netif_port_get_by_name(name); + if (!port) + return EDPVS_NOTEXIST; + ret = netif_get_multicast_addrs(port, out, outlen); + break; default: RTE_LOG(WARNING, NETIF, "[%s] invalid netif get cmd: %d\n", __func__, opt); @@ -5328,6 +5313,11 @@ static int set_port(struct netif_port *port, const netif_nic_set_t *port_cfg) else if (port_cfg->tc_ingress_off) port->flag &= (~NETIF_PORT_FLAG_TC_INGRESS); + if (port_cfg->lldp_on) + port->flag |= NETIF_PORT_FLAG_LLDP; + else if (port_cfg->lldp_off) + port->flag &= (~NETIF_PORT_FLAG_LLDP); + return EDPVS_OK; } diff --git a/src/netif_addr.c b/src/netif_addr.c index 9859e7810..41bd0080f 100644 --- a/src/netif_addr.c +++ b/src/netif_addr.c @@ -23,16 +23,18 @@ */ #include "netif.h" #include "netif_addr.h" +#include "conf/netif_addr.h" #include "kni.h" -static int __netif_hw_addr_add(struct netif_hw_addr_list *list, - const struct rte_ether_addr *addr) +int __netif_hw_addr_add(struct netif_hw_addr_list *list, + const struct rte_ether_addr *addr, uint16_t flags) { struct netif_hw_addr *ha; list_for_each_entry(ha, &list->addrs, list) { if (eth_addr_equal(&ha->addr, addr)) { rte_atomic32_inc(&ha->refcnt); + ha->flags |= flags; return EDPVS_OK; } } @@ -43,15 +45,15 @@ static int __netif_hw_addr_add(struct netif_hw_addr_list *list, rte_ether_addr_copy(addr, &ha->addr); rte_atomic32_set(&ha->refcnt, 1); - ha->sync_cnt = 0; + ha->flags = flags; list_add_tail(&ha->list, &list->addrs); list->count++; return EDPVS_OK; } -static int __netif_hw_addr_del(struct netif_hw_addr_list *list, - const struct rte_ether_addr *addr) +int __netif_hw_addr_del(struct netif_hw_addr_list *list, + const struct rte_ether_addr *addr, uint16_t flags) { struct netif_hw_addr *ha, *n; @@ -61,6 +63,8 @@ static int __netif_hw_addr_del(struct netif_hw_addr_list *list, list_del(&ha->list); list->count--; rte_free(ha); + } else { + ha->flags &= ~flags; } return EDPVS_OK; } @@ -81,25 +85,10 @@ static int __netif_hw_addr_sync(struct netif_hw_addr_list *to, eth_addr_dump(&ha->addr, mac, sizeof(mac)); /* for debug */ if (!ha->sync_cnt) { /* not synced to lower device */ - err = __netif_hw_addr_add(to, &ha->addr); + err = __netif_hw_addr_add(to, &ha->addr, 0); if (err == EDPVS_OK) { ha->sync_cnt++; rte_atomic32_inc(&ha->refcnt); - - /* - * when sync ha from upper to lower, - * we also need sync-back to lower's Linux kni device. - * if not, when lower's kni device mc-list changed, - * it may delete "synced" ha here by mistake. - * - * note on Linux two kni devices has no relationship. - * - * the whole logic should be: - * upper.kni -> uppper -> lower -> lower.kni - */ - if (kni_dev_exist(todev)) - linux_hw_mc_add(todev->kni.name, (uint8_t *)&ha->addr); - RTE_LOG(DEBUG, NETIF, "%s: sync %s to %s OK!\n", __func__, mac, todev->name); } else { @@ -111,14 +100,10 @@ static int __netif_hw_addr_sync(struct netif_hw_addr_list *to, /* both "ha->sync_cnt != 0" and "refcnt == 1" means * lower device is the only reference of this ha. * we can "unsync" from lower dev and remove it for upper. */ - err = __netif_hw_addr_del(to, &ha->addr); + err = __netif_hw_addr_del(to, &ha->addr, 0); if (err == EDPVS_OK) { - if (kni_dev_exist(todev)) - linux_hw_mc_del(todev->kni.name, (uint8_t *)&ha->addr); - RTE_LOG(DEBUG, NETIF, "%s: unsync %s to %s OK!\n", __func__, mac, todev->name); - list_del(&ha->list); rte_free(ha); from->count--; @@ -143,7 +128,8 @@ static int __netif_hw_addr_unsync(struct netif_hw_addr_list *to, static int __netif_hw_addr_sync_multiple(struct netif_hw_addr_list *to, struct netif_hw_addr_list *from, - struct netif_port *todev) + struct netif_port *todev, + int sync_cnt) { struct netif_hw_addr *ha, *n; int err = EDPVS_OK; @@ -153,14 +139,12 @@ static int __netif_hw_addr_sync_multiple(struct netif_hw_addr_list *to, eth_addr_dump(&ha->addr, mac, sizeof(mac)); /* for debug */ if (rte_atomic32_read(&ha->refcnt) == ha->sync_cnt) { - err = __netif_hw_addr_del(to, &ha->addr); + /* 'ha->refcnt == ha->sync_cnt' means the 'ha' has been removed from currecnt device + * and all references of this ha are from lower devices, so it's time to unsync. */ + err = __netif_hw_addr_del(to, &ha->addr, 0); if (err == EDPVS_OK) { - if (kni_dev_exist(todev)) - linux_hw_mc_del(todev->kni.name, (uint8_t *)&ha->addr); - RTE_LOG(DEBUG, NETIF, "%s: unsync %s to %s OK!\n", __func__, mac, todev->name); - ha->sync_cnt--; if (rte_atomic32_dec_and_test(&ha->refcnt)) { list_del(&ha->list); @@ -172,26 +156,12 @@ static int __netif_hw_addr_sync_multiple(struct netif_hw_addr_list *to, __func__, mac, todev->name); break; } - } else { - err = __netif_hw_addr_add(to, &ha->addr); + } else if (ha->sync_cnt < sync_cnt) { + /* sync to lower devices only once */ + err = __netif_hw_addr_add(to, &ha->addr, 0); if (err == EDPVS_OK) { ha->sync_cnt++; rte_atomic32_inc(&ha->refcnt); - - /* - * when sync ha from upper to lower, - * we also need sync-back to lower's Linux kni device. - * if not, when lower's kni device mc-list changed, - * it may delete "synced" ha here by mistake. - * - * note on Linux two kni devices has no relationship. - * - * the whole logic should be: - * upper.kni -> uppper -> lower -> lower.kni - */ - if (kni_dev_exist(todev)) - linux_hw_mc_add(todev->kni.name, (uint8_t *)&ha->addr); - RTE_LOG(DEBUG, NETIF, "%s: sync %s to %s OK!\n", __func__, mac, todev->name); } else { @@ -204,20 +174,30 @@ static int __netif_hw_addr_sync_multiple(struct netif_hw_addr_list *to, } static int __netif_hw_addr_unsync_multiple(struct netif_hw_addr_list *to, - struct netif_hw_addr_list *from) + struct netif_hw_addr_list *from, + int sync_cnt) { /* TODO: */ return EDPVS_INVAL; } -int __netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr) +int __netif_set_mc_list(struct netif_port *dev) { - return __netif_hw_addr_add(&dev->mc, addr); + if (!dev->netif_ops->op_set_mc_list) + return EDPVS_NOTSUPP; + + return dev->netif_ops->op_set_mc_list(dev); } -int __netif_mc_del(struct netif_port *dev, const struct rte_ether_addr *addr) +int netif_set_mc_list(struct netif_port *dev) { - return __netif_hw_addr_del(&dev->mc, addr); + int err; + + rte_rwlock_write_lock(&dev->dev_lock); + err = __netif_set_mc_list(dev); + rte_rwlock_write_unlock(&dev->dev_lock); + + return err; } int netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr) @@ -225,7 +205,7 @@ int netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr) int err; rte_rwlock_write_lock(&dev->dev_lock); - err = __netif_mc_add(dev, addr); + err = __netif_hw_addr_add(&dev->mc, addr, 0); if (err == EDPVS_OK) err = __netif_set_mc_list(dev); rte_rwlock_write_unlock(&dev->dev_lock); @@ -238,7 +218,7 @@ int netif_mc_del(struct netif_port *dev, const struct rte_ether_addr *addr) int err; rte_rwlock_write_lock(&dev->dev_lock); - err = __netif_mc_del(dev, addr); + err = __netif_hw_addr_del(&dev->mc, addr, 0); if (err == EDPVS_OK) err = __netif_set_mc_list(dev); rte_rwlock_write_unlock(&dev->dev_lock); @@ -271,8 +251,8 @@ void netif_mc_init(struct netif_port *dev) rte_rwlock_write_unlock(&dev->dev_lock); } -int __netif_mc_dump(struct netif_port *dev, - struct rte_ether_addr *addrs, size_t *naddr) +int __netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, + struct rte_ether_addr *addrs, size_t *naddr) { struct netif_hw_addr *ha; int off = 0; @@ -281,42 +261,64 @@ int __netif_mc_dump(struct netif_port *dev, return EDPVS_NOROOM; list_for_each_entry(ha, &dev->mc.addrs, list) - rte_ether_addr_copy(&ha->addr, &addrs[off++]); + if (!filter_flags || ha->flags & filter_flags) + rte_ether_addr_copy(&ha->addr, &addrs[off++]); *naddr = off; return EDPVS_OK; } -int netif_mc_dump(struct netif_port *dev, - struct rte_ether_addr *addrs, size_t *naddr) +int netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, + struct rte_ether_addr *addrs, size_t *naddr) { int err; rte_rwlock_read_lock(&dev->dev_lock); - err = __netif_mc_dump(dev, addrs, naddr); + err = __netif_mc_dump(dev, filter_flags, addrs, naddr); rte_rwlock_read_unlock(&dev->dev_lock); return err; } +static int __netif_mc_dump_all(struct netif_port *dev, uint16_t filter_flags, + struct netif_hw_addr_entry *addrs, int *naddr) +{ + struct netif_hw_addr *ha; + int off = 0; + + if (*naddr < dev->mc.count) + return EDPVS_NOROOM; + + list_for_each_entry(ha, &dev->mc.addrs, list) { + eth_addr_dump(&ha->addr, addrs[off].addr, sizeof(addrs[off].addr)); + addrs[off].refcnt = rte_atomic32_read(&ha->refcnt); + addrs[off].flags = ha->flags; + addrs[off].sync_cnt = ha->sync_cnt; + off++; + } + + *naddr = off; + return EDPVS_OK; +} + int __netif_mc_print(struct netif_port *dev, char *buf, int *len, int *pnaddr) { - struct rte_ether_addr addrs[NETIF_MAX_HWADDR]; - size_t naddr = NELEMS(addrs); + struct netif_hw_addr_entry addrs[NETIF_MAX_HWADDR]; + int naddr = NELEMS(addrs); int err, i; int strlen = 0; - err = __netif_mc_dump(dev, addrs, &naddr); + err = __netif_mc_dump_all(dev, 0, addrs, &naddr); if (err != EDPVS_OK) goto errout; for (i = 0; i < naddr && *len > strlen; i++) { err = snprintf(buf + strlen, *len - strlen, - " link %02x:%02x:%02x:%02x:%02x:%02x\n", - addrs[i].addr_bytes[0], addrs[i].addr_bytes[1], - addrs[i].addr_bytes[2], addrs[i].addr_bytes[3], - addrs[i].addr_bytes[4], addrs[i].addr_bytes[5]); + " link %s %srefcnt %d, synced %d\n", + addrs[i].addr, + addrs[i].flags & HW_ADDR_F_FROM_KNI ? "(kni) ": "", + addrs[i].refcnt, addrs[i].sync_cnt); if (err < 0) { err = EDPVS_NOROOM; goto errout; @@ -335,6 +337,33 @@ int __netif_mc_print(struct netif_port *dev, return err; } +int netif_get_multicast_addrs(struct netif_port *dev, void **out, size_t *outlen) +{ + int err; + size_t len; + struct netif_hw_addr_array *array; + + rte_rwlock_read_lock(&dev->dev_lock); + len = sizeof(*array) + dev->mc.count * sizeof(struct netif_hw_addr_entry); + array = rte_zmalloc(NULL, len, RTE_CACHE_LINE_SIZE); + if (unlikely(!array)) { + err = EDPVS_NOMEM; + } else { + array->count = dev->mc.count; + err = __netif_mc_dump_all(dev, 0, array->entries, &array->count); + } + rte_rwlock_read_unlock(&dev->dev_lock); + + if (err != EDPVS_OK) { + *out = NULL; + *outlen = 0; + } else { + *out = array; + *outlen = len; + } + return err; +} + int netif_mc_print(struct netif_port *dev, char *buf, int *len, int *pnaddr) { @@ -400,11 +429,11 @@ int netif_mc_unsync(struct netif_port *to, struct netif_port *from) return err; } -int __netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from) +int __netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt) { int err; - err = __netif_hw_addr_sync_multiple(&to->mc, &from->mc, to); + err = __netif_hw_addr_sync_multiple(&to->mc, &from->mc, to, sync_cnt); if (err == EDPVS_OK) err = __netif_set_mc_list(to); @@ -412,14 +441,15 @@ int __netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from) } int netif_mc_sync_multiple(struct netif_port *to, - struct netif_port *from) + struct netif_port *from, + int sync_cnt) { int err; rte_rwlock_write_lock(&to->dev_lock); rte_rwlock_write_lock(&from->dev_lock); - err = __netif_mc_sync_multiple(to, from); + err = __netif_mc_sync_multiple(to, from, sync_cnt); rte_rwlock_write_unlock(&from->dev_lock); rte_rwlock_write_unlock(&to->dev_lock); @@ -427,25 +457,27 @@ int netif_mc_sync_multiple(struct netif_port *to, return err; } -int __netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from) +int __netif_mc_unsync_multiple(struct netif_port *to, + struct netif_port *from, + int sync_cnt) { int err; - err = __netif_hw_addr_unsync_multiple(&to->mc, &from->mc); + err = __netif_hw_addr_unsync_multiple(&to->mc, &from->mc, sync_cnt); if (err == EDPVS_OK) err = __netif_set_mc_list(to); return err; } -int netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from) +int netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt) { int err; rte_rwlock_write_lock(&to->dev_lock); rte_rwlock_write_lock(&from->dev_lock); - err = __netif_mc_unsync_multiple(to, from); + err = __netif_mc_unsync_multiple(to, from, sync_cnt); rte_rwlock_write_unlock(&from->dev_lock); rte_rwlock_write_unlock(&to->dev_lock); diff --git a/src/route.c b/src/route.c index 00354f7c8..f48d82fa2 100644 --- a/src/route.c +++ b/src/route.c @@ -671,8 +671,12 @@ static struct dpvs_sockopts route_sockopts = { static int route_lcore_init(void *arg) { int i; + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; - if (!rte_lcore_is_enabled(rte_lcore_id())) + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; for (i = 0; i < LOCAL_ROUTE_TAB_SIZE; i++) @@ -684,7 +688,12 @@ static int route_lcore_init(void *arg) static int route_lcore_term(void *arg) { - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; return route_lcore_flush(); diff --git a/src/scheduler.c b/src/scheduler.c index d446f378b..338cfa238 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -188,6 +188,9 @@ static int dpvs_job_loop(void *arg) thres_time = BIG_LOOP_THRESH_MASTER; #endif + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + /* skip irrelative job loops */ if (role == LCORE_ROLE_MAX) return EDPVS_INVAL; diff --git a/src/tc/cls_match.c b/src/tc/cls_match.c index ab7ec357d..8f5bcf6ec 100644 --- a/src/tc/cls_match.c +++ b/src/tc/cls_match.c @@ -25,6 +25,7 @@ #include #include #include +#include "sctp/sctp.h" #include "netif.h" #include "vlan.h" #include "tc/tc.h" @@ -54,6 +55,7 @@ static int match_classify(struct tc_cls *cls, struct rte_mbuf *mbuf, struct ip6_hdr *ip6h = NULL; struct tcphdr *th; struct udphdr *uh; + struct sctphdr *sh; uint8_t l4_proto = 0; int offset = sizeof(*eh); __be16 pkt_type = eh->ether_type; @@ -175,6 +177,17 @@ static int match_classify(struct tc_cls *cls, struct rte_mbuf *mbuf, dport = uh->dest; break; + case IPPROTO_SCTP: + if (mbuf_may_pull(mbuf, offset + sizeof(struct sctphdr)) != 0) { + err = TC_ACT_SHOT; + goto done; + } + + sh = rte_pktmbuf_mtod_offset(mbuf, struct sctphdr *, offset); + sport = sh->src_port; + dport = sh->dest_port; + break; + default: /* priv->proto is not assigned */ goto match; } diff --git a/src/timer.c b/src/timer.c index dd3485375..b3ae9d505 100644 --- a/src/timer.c +++ b/src/timer.c @@ -412,7 +412,12 @@ static int timer_term_schedler(struct timer_scheduler *sched) static int timer_lcore_init(void *arg) { - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; return timer_init_schedler(&RTE_PER_LCORE(timer_sched), rte_lcore_id()); @@ -420,7 +425,12 @@ static int timer_lcore_init(void *arg) static int timer_lcore_term(void *arg) { - if (!rte_lcore_is_enabled(rte_lcore_id())) + lcoreid_t cid = rte_lcore_id(); + + if (cid >= DPVS_MAX_LCORE) + return EDPVS_OK; + + if (!rte_lcore_is_enabled(cid)) return EDPVS_DISABLED; return timer_term_schedler(&RTE_PER_LCORE(timer_sched)); diff --git a/src/vlan.c b/src/vlan.c index 1b06aea3b..955b0b840 100644 --- a/src/vlan.c +++ b/src/vlan.c @@ -75,6 +75,21 @@ static int alloc_vlan_info(struct netif_port *dev) return EDPVS_OK; } +static int vlan_dev_init(struct netif_port *dev) +{ + int err; + struct inet_device *idev = dev_get_idev(dev); + + err = idev_addr_init(idev); + if (err != EDPVS_OK) { + idev_put(idev); + return err; + } + + idev_put(idev); + return EDPVS_OK; +} + static int vlan_xmit(struct rte_mbuf *mbuf, struct netif_port *dev) { struct vlan_dev_priv *vlan = netif_priv(dev); @@ -171,6 +186,7 @@ static int vlan_get_stats(struct netif_port *dev, struct rte_eth_stats *stats) } static struct netif_ops vlan_netif_ops = { + .op_init = vlan_dev_init, .op_xmit = vlan_xmit, .op_set_mc_list = vlan_set_mc_list, .op_get_queue = vlan_get_queue, @@ -228,8 +244,8 @@ int vlan_add_dev(struct netif_port *real_dev, const char *ifname, } /* allocate and register netif device */ - dev = netif_alloc(sizeof(struct vlan_dev_priv), name_buf, - real_dev->nrxq, real_dev->ntxq, vlan_setup); + dev = netif_alloc(NETIF_PORT_ID_INVALID, sizeof(struct vlan_dev_priv), + name_buf, real_dev->nrxq, real_dev->ntxq, vlan_setup); if (!dev) { err = EDPVS_NOMEM; goto out; @@ -241,6 +257,7 @@ int vlan_add_dev(struct netif_port *real_dev, const char *ifname, dev->flag &= ~NETIF_PORT_FLAG_TX_IP_CSUM_OFFLOAD; dev->flag &= ~NETIF_PORT_FLAG_TX_TCP_CSUM_OFFLOAD; dev->flag &= ~NETIF_PORT_FLAG_TX_UDP_CSUM_OFFLOAD; + dev->flag &= ~NETIF_PORT_FLAG_LLDP; dev->type = PORT_TYPE_VLAN; rte_ether_addr_copy(&real_dev->addr, &dev->addr); diff --git a/test/ipset/dpvs-agent.sh b/test/ipset/dpvs-agent.sh new file mode 100755 index 000000000..8a22a82d0 --- /dev/null +++ b/test/ipset/dpvs-agent.sh @@ -0,0 +1,158 @@ +#!/bin/env sh +# + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"bitmap:ip,mac","Name":"ttt","CreationOptions":{"Family":"ipv4","Comment":true,"Range":"192.168.88.0/24"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:ip,mac","Name":"ttt","Entries":[{"Entry":"192.168.88.1,AA:bb:CC:11:22:33"},{"Entry":"192.168.88.100","Comment":"no mac","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"bitmap:ip,mac","Member":{"Entry":"192.168.88.1,AA:bb:CC:11:22:33"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:ip,mac","Name":"ttt","Entries":[{"Entry":"192.168.88.100,AA:bb:CC:11:22:33"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:ip,mac","Name":"ttt","Entries":[{"Entry":"192.168.88.100","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"bitmap:port","Name":"ttt","CreationOptions":{"Comment":true,"Range":"10000-20000"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:port","Name":"ttt","Entries":[{"Entry":"tcp:10000-10002"},{"Entry":"tcp:10888","Comment":"single","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"bitmap:port","Member":{"Entry":"tcp:12222"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:port","Name":"ttt","Entries":[{"Entry":"tcp:10000-10008"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"bitmap:port","Name":"ttt","Entries":[{"Entry":"tcp:10003","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv4","HashSize": 128,"HashMaxElem": 10001}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"192.168.88.100/30","Comment":"a cidr"},{"Entry":"10.64.68.1-10.64.68.3","Comment":"a range","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip","Member":{"Entry":"192.168.88.100"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"192.168.88.100/30","Comment":"a cidr"},{"Entry":"10.64.68.10-10.64.68.14","Comment":"a range","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"192.168.88.100-192.168.88.128","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv6","HashSize": 256,"HashMaxElem": 20001}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"2001::B","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip","Member":{"Entry":"2001::a"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"2001::b","Comment":"replace","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip","Name":"ttt","Entries":[{"Entry":"2001::a","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv4"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"192.168.88.128/26","Comment":"net1"},{"Entry":"10.64.0.10-10.64.0.20","Comment":"a net range","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net","Member":{"Entry":"192.168.88.100"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"192.168.88.128/26","Comment":"net1"},{"Entry":"192.168.88.164/30","Comment":"net1 nomatch","Options":{"NoMatch":true}},{"Entry":"10.64.0.10-10.64.0.20","Comment":"a net range","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"192.168.88.192/28","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"2001::/64","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net","Member":{"Entry":"2001::66"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"2002::/64","Comment":"replace","Options":{"Force":false}},{"Entry":"2002::ff:0:0/96","Comment":"net1 nomatch","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net","Name":"ttt","Entries":[{"Entry":"2002::/64","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv4"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"192.168.88.0/24,tcp:8080","Comment":"net cidr"},{"Entry":"10.64.0.10-10.64.0.20","Comment":"net range","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Member":{"Entry":"192.168.88.100,tcp:8080"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"192.168.88.128/26,udp:80-82","Comment":"net1"},{"Entry":"192.168.88.164/30,udp:80","Comment":"net1 nomatch","Options":{"NoMatch":true}},{"Entry":"10.64.0.10-10.64.0.20,tcp:6600","Comment":"a net range","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"192.168.88.128/26,udp:81","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Member":{"Entry":"2001::66,tcp:8082"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"2002::/64,udp:80-83","Comment":"replace","Options":{"Force":false}},{"Entry":"2002::ff:0:0/96,udp:80","Comment":"net1 nomatch","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port","Name":"ttt","Entries":[{"Entry":"2002::/64,udp:82","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv4"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"192.168.88.0/24,tcp:8080,dpdk0","Comment":"net cidr"},{"Entry":"10.64.0.10-10.64.0.20,tcp:80-82,dpdk0","Comment":"net range","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Member":{"Entry":"192.168.88.100,tcp:8080,dpdk0"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"192.168.88.0/24,tcp:80-82,dpdk0","Comment":"net cidr"},{"Entry":"10.64.0.10-10.64.0.20,tcp:8080,dpdk0","Comment":"net range","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"192.168.88.0/24,tcp:81,dpdk0","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","CreationOptions":{"Comment":false,"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083,dpdk0","Options":{"Force":false}},{"Entry":"2002::/64,udp:6600,dpdk0","Comment":"xxxxx","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Member":{"Entry":"2001::66,tcp:8082,dpdk0"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"2002::/64,udp:80-83,dpdk0","Comment":"zzzzz","Options":{"Force":false}},{"Entry":"2002::ff:0:0/96,udp:80,dpdk0","Comment":"net1 nomatch","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,iface","Name":"ttt","Entries":[{"Entry":"2002::/64,udp:82,dpdk0","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv4"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"192.168.1.16/31,tcp:8080-8081,192.168.2.100-192.168.2.102","Comment":"net-port-range"},{"Entry":"10.64.0.10-10.64.0.20,udp:6600,112.112.112.112","Comment":"udp","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Member":{"Entry":"192.168.1.17,tcp:8080,192.168.2.100"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"192.168.1.16/31,tcp:8080-8081,192.168.2.100-192.168.2.102","Comment":"net-port-range"},{"Entry":"10.64.88.0,udp:6600,112.112.112.112","Comment":"udp","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"192.1681.16/31,tcp:8081,192.168.2.101","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","CreationOptions":{"Comment":false,"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"2001::4444,tcp:8080-8083,2002::7777","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Member":{"Entry":"2001::4444,tcp:8082,2002::7777"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"2002::aaaa,udp:80-83,2001::6666","Comment":"xxxxx","Options":{"Force":false}},{"Entry":"2002::ff:1,tcp:80,2001::ee:2","Comment":"net1 nomatch","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"2002::aaaa,udp:82,2001::6666","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,ip","Name":"ttt","Entries":[{"Entry":"2002::aaaa,udp:80-83,2001::6666","Comment":"xxxxx","Options":{"Force":true}},{"Entry":"2002::ff:1,tcp:80,2001::ee:2","Comment":"net1 nomatch","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt"}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.5-192.168.1.6,tcp:8081-8082,192.168.88.0/24"},{"Entry":"10.64.100.100/30,udp:6600,10.64.200.0/24","Comment":"udp","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Member":{"Entry":"192.168.1.6,tcp:8081,192.168.88.111"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.4/30,80,192.168.88.0/24"},{"Entry":"10.64.100.100/30,udp:6688,10.64.100.0/24","Comment":"udp","Options":{"Force":false}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.6/31,80,192.168.88.0/24"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv6","HashSize":64,"HashMaxElem":20000}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"2001::1,tcp:8080-8083,2002::0/64"},{"Entry":"2001::1,tcp:8080,2002::FFFF:0/112","Options":{"NoMatch":true}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Member":{"Entry":"2001::1,tcp:8082,2002::FFFF:1"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"2001::1,tcp:8080-8083,2002::0/64"},{"Entry":"2001::1,tcp:8080,2001:FFFF::0/80","Comment":"replaced","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:ip,port,net","Name":"ttt","Entries":[{"Entry":"2001::1,tcp:8080,2002::0/64"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt"}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.0/24,tcp:8080-8083,192.168.2.0/24"},{"Entry":"10.64.96.0/21,udp:6600,10.132.80.0/21","Comment":"udp","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Member":{"Entry":"192.168.1.4,tcp:8080,192.168.2.254"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.0/24,tcp:8080-8083,192.168.2.0/24"},{"Entry":"10.64.96.0/21,udp:6600,10.132.80.0/21","Comment":"udp","Options":{"Force":false}},{"Entry":"10.64.97.0/24,udp:6600,10.132.82.0/24","Comment":"udp","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"192.168.1.0/24,tcp:8082,192.168.2.0/24"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","CreationOptions":{"Comment":true,"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083,2002::0/64"},{"Entry":"2001::/120,tcp:8080,2002::FFFF:0/112","Options":{"NoMatch":true}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Member":{"Entry":"2001::1,tcp:8082,2002::FFFF:1"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083,2002::0/64"},{"Entry":"2001::/112,tcp:8080,2002::FFFF:0/112","Comment":"replaced","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8081,2002::/64"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +## <> +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","CreationOptions":{"Comment":true}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"192.168.1.0/24,tcp:8080-8083,192.168.2.0/24,tcp:12345"},{"Entry":"10.64.96.0/21,udp:53,10.132.80.0/21,udp:6600-6601","Comment":"udp","Options":{"Force":false}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Member":{"Entry":"192.168.1.4,tcp:8080,192.168.2.254,tcp:12345"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"192.168.1.0/24,tcp:8080-8083,192.168.2.0/24,tcp:12345"},{"Entry":"10.64.96.0/21,udp:53,10.132.80.0/21,udp:6600-6601","Comment":"udp","Options":{"Force":false}},{"Entry":"10.64.96.0/24,udp:53,10.132.80.86/24,udp:6601","Comment":"add exceptions","Options":{"Nomatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"10.64.96.0/21,udp:53,10.132.80.0/21,udp:6600-6601"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt + +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","CreationOptions":{"Family":"ipv6"}}' +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083,2002::0/64,tcp:80","Options":{"Force":true}},{"Entry":"2001::/120,tcp:8080,2002::FFFF:0/112,tcp:80","Options":{"NoMatch":true,"Force":true}}]}' +curl -X GET http://127.0.0.1:8866/v2/ipset/ttt | jq | more +curl -X POST http://127.0.0.1:8866/v2/ipset/ttt/cell -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Member":{"Entry":"2001::1,tcp:8082,2002::FFFF:1,tcp:80"}}' +curl -X PUT http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8080-8083,2002::0/64,tcp:80"},{"Entry":"2001::/112,tcp:8080,2002::FFFF:0/112,tcp:80","Comment":"replaced","Options":{"NoMatch":true}}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt/member -H "Content-Type: application/json" -d '{"Type":"hash:net,port,net,port","Name":"ttt","Entries":[{"Entry":"2001::/64,tcp:8082,2002::/64,tcp:80"}]}' +curl -X DELETE http://127.0.0.1:8866/v2/ipset/ttt diff --git a/test/quic/.gitignore b/test/quic/.gitignore new file mode 100644 index 000000000..a52aaf740 --- /dev/null +++ b/test/quic/.gitignore @@ -0,0 +1,4 @@ +client/quic-client +server/quic-server +http3/h3client/h3client +http3/h3server/h3server diff --git a/test/quic/0001-quic-test-codes-for-conn-migration.patch b/test/quic/0001-quic-test-codes-for-conn-migration.patch new file mode 100644 index 000000000..a83f2e7c6 --- /dev/null +++ b/test/quic/0001-quic-test-codes-for-conn-migration.patch @@ -0,0 +1,63 @@ +From 462d64d15bed454a7fc1a367490910018b583003 Mon Sep 17 00:00:00 2001 +From: ywc689 +Date: Wed, 15 May 2024 10:33:44 +0800 +Subject: [PATCH] quic test codes for conn migration + +Signed-off-by: ywc689 +--- + src/ipvs/ip_vs_core.c | 14 ++++++++++++++ + src/ipvs/ip_vs_proto_udp.c | 6 ++++++ + 2 files changed, 20 insertions(+) + +diff --git a/src/ipvs/ip_vs_core.c b/src/ipvs/ip_vs_core.c +index b92e984..08c2479 100644 +--- a/src/ipvs/ip_vs_core.c ++++ b/src/ipvs/ip_vs_core.c +@@ -38,6 +38,7 @@ + #include "ipvs/proto_udp.h" + #include "route6.h" + #include "ipvs/redirect.h" ++#include "ipvs/quic.h" + + static inline int dp_vs_fill_iphdr(int af, struct rte_mbuf *mbuf, + struct dp_vs_iphdr *iph) +@@ -990,6 +991,19 @@ static int __dp_vs_in(void *priv, struct rte_mbuf *mbuf, + return INET_DROP; + } + ++ // TODO: remove the test codes ++ if (iph.proto == IPPROTO_UDP && conn && dir == DPVS_CONN_DIR_INBOUND) { ++ int err; ++ struct quic_server qsvr; ++ char buf[256]; ++ ++ err = quic_parse_server(mbuf, &iph, &qsvr); ++ if (err == EDPVS_OK) { ++ quic_dump_server(&qsvr, buf, sizeof(buf)); ++ RTE_LOG(INFO, IPVS, "*** got quic server in mbuf: %s ***\n", buf); ++ } ++ } ++ + /* + * The connection is not locally found, however the redirect is found so + * forward the packet to the remote redirect owner core. +diff --git a/src/ipvs/ip_vs_proto_udp.c b/src/ipvs/ip_vs_proto_udp.c +index df58fc9..73a88bf 100644 +--- a/src/ipvs/ip_vs_proto_udp.c ++++ b/src/ipvs/ip_vs_proto_udp.c +@@ -180,6 +180,12 @@ static int udp_conn_sched(struct dp_vs_proto *proto, + struct quic_server qsvr = { 0 }; + int err = quic_parse_server(mbuf, iph, &qsvr); + if (likely(err == EDPVS_OK)) { ++ // TODO: remove the test codes ++ { ++ qsvr.wildcard = 16; ++ qsvr.addr.in.s_addr = htonl(0x581e); //x.x.88.30 ++ qsvr.port = htons(4141); ++ } + if (qsvr.wildcard > 0) { + *conn = quic_schedule(svc, &qsvr, iph, mbuf); + if (*conn) +-- +1.8.3.1 + diff --git a/test/quic/Makefile b/test/quic/Makefile new file mode 100644 index 000000000..d5d229162 --- /dev/null +++ b/test/quic/Makefile @@ -0,0 +1,29 @@ +TARGETS := quic-client quic-server h3client h3server + +GO ?= go +LD_FLAGS = -ldflags="-s -w" +GO_BUILD = CGO_ENABLED=0 $(GO) build $(LD_FLAGS) +RM ?= rm + + +.PHONY: all $(TARGET) clean version_notes + +all: version_notes $(TARGETS) + +quic-client: client/quic-client.go + $(GO_BUILD) -o $@ $< + +quic-server: server/quic-server.go + $(GO_BUILD) -o $@ $< + +h3client: http3/h3client/h3client.go + $(GO_BUILD) -o $@ $< + +h3server: http3/h3server/h3server.go + $(GO_BUILD) -o $@ $< + +clean: + @-$(RM) $(TARGETS) + +version_notes: + $(info "Notes: $(shell go version), v1.21+ is required") diff --git a/test/quic/README.md b/test/quic/README.md new file mode 100644 index 000000000..a113c0abd --- /dev/null +++ b/test/quic/README.md @@ -0,0 +1,11 @@ +The test programs in this directory are built with QUIC library [quic-go](https://github.com/quic-go/quic-go). + +The version requirements are shown as below. +* Quic-go: v0.42.0 +* Golang: v1.21.8 + +Quic-go may not well support ECN in such distros as Centos 7 (refer to [issue #4396](https://github.com/quic-go/quic-go/issues/4396) for details), in which case the ECN should be disabled using environment varible `QUIC_GO_DISABLE_ECN`. + +```sh +export QUIC_GO_DISABLE_ECN=true +``` diff --git a/test/quic/client/quic-client.go b/test/quic/client/quic-client.go new file mode 100644 index 000000000..764910597 --- /dev/null +++ b/test/quic/client/quic-client.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + "crypto/tls" + "flag" + "fmt" + "io" + "log" + "net" + "os" + "runtime/trace" + + "github.com/quic-go/quic-go" +) + +func main() { + servAddr := flag.String("server", ":4242", "quic server address") + keyLogFile := flag.String("keylog", "", "key log file") + traceFile := flag.String("trace", "", "trace file name") + flag.Parse() + + if *traceFile != "" { + tracef, err := os.Create(*traceFile) + if err != nil { + log.Fatalf("failed to create trace output file: %v", err) + } + defer tracef.Close() + err = trace.Start(tracef) + if err != nil { + log.Fatalf("failed to start trace: %v", err) + } + defer trace.Stop() + } + + fmt.Printf("target server: %s\n", *servAddr) + + tlsConf := &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{"quic-echo-example"}, + } + if *keyLogFile != "" { + keyLog, err := os.OpenFile(*keyLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatal(err) + } + defer keyLog.Close() + tlsConf.KeyLogWriter = keyLog + } + + ctx := context.Background() + /* + conn, err := quic.DialAddr(ctx, *servAddr, tlsConf, nil) + if err != nil { + log.Fatal("Cannot dial QUIC server:", err) + } + defer conn.CloseWithError(0, "") + */ + + serverAddr, err := net.ResolveUDPAddr("udp", *servAddr) + if err != nil { + log.Fatal("ServerAddr resolution fail:", err) + } + + listenAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + listener, err := net.ListenUDP("udp", listenAddr) + if err != nil { + log.Fatal("Listener creation falil:", err) + } + defer listener.Close() + + conn, err := quic.Dial(ctx, listener, serverAddr, tlsConf, nil) + /* + cidGenerator := cid.NewDpvsQCID(10, 4, 0, nil, 0) + transport := &quic.Transport{ + Conn: listener, + ConnectionIDGenerator: cidGenerator, + } + conn, err := transport.Dial(ctx, serverAddr, tlsConf, nil) + */ + if err != nil { + log.Fatal("Cannot dial QUIC server:", err) + } + + stream, err := conn.OpenStreamSync(ctx) + if err != nil { + log.Fatal("Cannot open QUIC stream:", err) + } + defer stream.Close() + + message := []byte("Hello, QUIC Server!") + _, err = stream.Write(message) + if err != nil { + log.Fatal("Cannot write to QUIC stream:", err) + } + + buffer := make([]byte, len(message)) + _, err = io.ReadFull(stream, buffer) + if err != nil { + log.Fatal("Cannot read from QUIC stream:", err) + } + + fmt.Printf("Server says: %s\n", buffer) + + // TODO: Support connection migration. + // Awaiting quic-go support the feature https://github.com/quic-go/quic-go/issues/3990. +} diff --git a/test/quic/go.mod b/test/quic/go.mod new file mode 100644 index 000000000..4b60204d2 --- /dev/null +++ b/test/quic/go.mod @@ -0,0 +1,23 @@ +module quic-test + +go 1.21.8 + +require ( + github.com/quic-go/quic-go v0.43.1 + golang.org/x/sys v0.20.0 +) + +require ( + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + go.uber.org/mock v0.4.0 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/tools v0.9.1 // indirect +) diff --git a/test/quic/go.sum b/test/quic/go.sum new file mode 100644 index 000000000..0926d4fc4 --- /dev/null +++ b/test/quic/go.sum @@ -0,0 +1,214 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= +github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/test/quic/http3/certs/cert.pem b/test/quic/http3/certs/cert.pem new file mode 100644 index 000000000..822bc32c4 --- /dev/null +++ b/test/quic/http3/certs/cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzDCCArSgAwIBAgIJALD9Trd6ieu1MA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD +VQQGEwJDTjERMA8GA1UECAwIU2hhbmdIYWkxEjAQBgNVBAcMCUNoYW5nTmluZzEO +MAwGA1UECgwFSVFJWUkxEDAOBgNVBAsMB0lJRy9RTEIxITAfBgkqhkiG9w0BCQEW +Enl1d2VuY2hhb0BxaXlpLmNvbTEUMBIGA1UEAwwLKi5pcWl5aS5jb20wHhcNMjQw +NTA2MDY0MjIxWhcNMzQwNTA0MDY0MjIxWjCBjzELMAkGA1UEBhMCQ04xETAPBgNV +BAgMCFNoYW5nSGFpMRIwEAYDVQQHDAlDaGFuZ05pbmcxDjAMBgNVBAoMBUlRSVlJ +MRAwDgYDVQQLDAdJSUcvUUxCMSEwHwYJKoZIhvcNAQkBFhJ5dXdlbmNoYW9AcWl5 +aS5jb20xFDASBgNVBAMMCyouaXFpeWkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA0+nv99AO31eiPQ6jZpdT2nn3AMlw3rq/JfYP2cFerbJqBE9p +98yNkgLYVMOSSpSHtZT3MckiwEUTX498CZ1zfFbsFoW9o+waJVV7swtWiqvquf7v +UdX8dknJQxxsnPhvEyesI7UIPhBQZ16J+rQW9TM30BSrl5Mb2BPS4PgvTeMsKDU6 +2lhbyEnEYepr32nRnwkz1QBflcvXzmPWbly8GUnQVYQqKdNtGT3yfyiKqJUZ5b2L +3WdvTwOxmjIQi1perwfFl1OFlXbCKv6VtkbaHZ27rBETECaLJ7vrRf6+USoo5RnB +SngSXrPq6BN9/u/kLSkLb+qH451lunrCEPLcSQIDAQABoykwJzAlBgNVHREEHjAc +ggsqLmlxaXlpLmNvbYINKi5xaXlpLmRvbWFpbjANBgkqhkiG9w0BAQsFAAOCAQEA +jQguPoHjs2r2JXob4B66gn5fTiLMsfLz/c66WDvS+uECCfcmubGY8IcB2eYUF1ut +gLDjfycxamjBK5iSBKLxkwvtG5hUbkY8kuPcx0I4G6peHWaGdZGUOVOjACZkoWKT +ztgD3sdr7M0FpS8dTomtT3jrEFUbmNGjlt+BvPVP5yx9PYR3vfLvRwgKzQzVXgZs +pgTL6WWhFEeAEC2r0y0u+j7Kj0p1RgFyVT5l56LVLXhqdD7dsddKKGRU435kfFem +F5K/Q7XPJUa3aixIni//LKc7XQ2Xlu4iSO7MWNKR97gaoVakVp1PWh/c4LVBLx6M +nMtHmVzrQii86eQI79OlOA== +-----END CERTIFICATE----- diff --git a/test/quic/http3/certs/gen.sh b/test/quic/http3/certs/gen.sh new file mode 100755 index 000000000..1122e8b1c --- /dev/null +++ b/test/quic/http3/certs/gen.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# + +openssl genrsa -out key.pem 2048 +openssl req -new -key key.pem -out req.csr -config san.conf +openssl x509 -req -days 3650 -in req.csr -signkey key.pem -out cert.pem -extensions req_ext -extfile san.conf diff --git a/test/quic/http3/certs/key.pem b/test/quic/http3/certs/key.pem new file mode 100644 index 000000000..057d77dff --- /dev/null +++ b/test/quic/http3/certs/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA0+nv99AO31eiPQ6jZpdT2nn3AMlw3rq/JfYP2cFerbJqBE9p +98yNkgLYVMOSSpSHtZT3MckiwEUTX498CZ1zfFbsFoW9o+waJVV7swtWiqvquf7v +UdX8dknJQxxsnPhvEyesI7UIPhBQZ16J+rQW9TM30BSrl5Mb2BPS4PgvTeMsKDU6 +2lhbyEnEYepr32nRnwkz1QBflcvXzmPWbly8GUnQVYQqKdNtGT3yfyiKqJUZ5b2L +3WdvTwOxmjIQi1perwfFl1OFlXbCKv6VtkbaHZ27rBETECaLJ7vrRf6+USoo5RnB +SngSXrPq6BN9/u/kLSkLb+qH451lunrCEPLcSQIDAQABAoIBACsyQX8jQxTVuTV2 ++WndKPOc7vOTHFXafUJQsRRzLUh82M1+HpyrbqQ3vj8xCm33bt5dujHEzTeiHPva +tK8FEFhlI4THyEtUwlOf5DIv+nkT6Cn3xHLLCsZV7henAKDSp1mhOZ6htUdpbepU +RA39jbx3r0XUINIp44AdMlw3WvUmHZCRU2sKLEanrmx8FOvQ6eDyAbo3qNoo/AGI +2iTrM1xlHpSQjHg6DVZ+2XDp/VHMZTTDPw9DkgugRdmFKR06apxh6RIVNbJOLfAR +Iyx0lNHo5weEWW1nQEC+bXfgejHhcx5HrTRKqbqeBpUxx2NEZKW7TTik11jUcHPR +tlTXoQECgYEA68HVOWfK47m58S226VBv59Egtu/y8TRv3iyS2ONhFkRD8ZORd2Gi +NrwSPQeF1JIpRJeCoNSXsbdi69qmZhKUW4Vx16OdhZG0vsPsK2LRaoy+A+maX2Ye +ZHAnp7nIDG3KBfzy3PCXC4SVWlxxcw3BTECVPtGxCeVEgZkGB9AGUekCgYEA5hwB +TUTWVxmBOKGOLptb17eQ8BlcP2o8JFsbcft7pum6ouECS+ZJmJ80BuBzFiMc18Fk +S28rM0ACF8BK8J7XJbK+xs1IlTpbHzQdo+tBImfjqqTeWyx2TrEDKB6VIRqhLQyf +jYYNcktSEjy2XRfvG5KC/oAs9axo7N02hW3wW2ECgYEAjU9HcPsnf0vpiggupLZT +/Q06oKw+YBlgHDl3Y40WunP8jaY4AOiChHBCNlZ1/y4EklqGL8R9kEYtgtUx++iT +CDB6RhiJ6G+neNiSjIbUoxrtIgc5QolBGk6nVj9jCyAbgW9WWtvSjVLQ+rKCRcYu +4HetfVPO2/GSGGQSW0hzIVECgYEAy8zp6kGZhLL2G+4aO3UltrzCBaSwawnwElMO +z7joH0DLKA8ZNZfUfvQh5CVOSMD4fq6t4ZGoNU/vipGozcwgySayiOiv7Fsu8Uf7 +KH7nxU01+qDivuV2MuPb4+CSPCuVrIyNk46ywhOrsLNM4M6d21G76yQircPxejfC +XhKs2oECgYEAvqmwICb/UKLwmQ39IY8o0/XA21oZFOTH28qS9ah7uCwNEUl5FmHW +oN6sgiaZyl8uxA/SoB2RBaJy4BxhyL+KEehrhhIFMVBAlNkgf36YDFS3XzoamKIB +Aoev43M9/VqXUM5xUMCOp7Dxo6NPv9uVio+VyVZD7i+3ZNFbhEgU4Rk= +-----END RSA PRIVATE KEY----- diff --git a/test/quic/http3/certs/req.csr b/test/quic/http3/certs/req.csr new file mode 100644 index 000000000..67bdceb87 --- /dev/null +++ b/test/quic/http3/certs/req.csr @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIDDTCCAfUCAQAwgY8xCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ0hhaTES +MBAGA1UEBwwJQ2hhbmdOaW5nMQ4wDAYDVQQKDAVJUUlZSTEQMA4GA1UECwwHSUlH +L1FMQjEhMB8GCSqGSIb3DQEJARYSeXV3ZW5jaGFvQHFpeWkuY29tMRQwEgYDVQQD +DAsqLmlxaXlpLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANPp +7/fQDt9Xoj0Oo2aXU9p59wDJcN66vyX2D9nBXq2yagRPaffMjZIC2FTDkkqUh7WU +9zHJIsBFE1+PfAmdc3xW7BaFvaPsGiVVe7MLVoqr6rn+71HV/HZJyUMcbJz4bxMn +rCO1CD4QUGdeifq0FvUzN9AUq5eTG9gT0uD4L03jLCg1OtpYW8hJxGHqa99p0Z8J +M9UAX5XL185j1m5cvBlJ0FWEKinTbRk98n8oiqiVGeW9i91nb08DsZoyEItaXq8H +xZdThZV2wir+lbZG2h2du6wRExAmiye760X+vlEqKOUZwUp4El6z6ugTff7v5C0p +C2/qh+OdZbp6whDy3EkCAwEAAaA4MDYGCSqGSIb3DQEJDjEpMCcwJQYDVR0RBB4w +HIILKi5pcWl5aS5jb22CDSoucWl5aS5kb21haW4wDQYJKoZIhvcNAQELBQADggEB +AH78c1QWE2+U4WcnHVBrXM0sNNuSx2ZszPXeb15fg+DQnYTtSKihSjrnhZ1jtyRT +jaMILXhz0CAdWB0mA9AqBCmq4CxDV0iJR7v8ndtLaLFCRAveHY9MPfrjY9jByLLO +Rv16bHyHeKmrrrFSxstmuJmPP7OJgqSuJKNdwEvIXkvlh3I21HZoS/jksoMMc9E5 +2m97k2jHGV5Jqs7W6SsDWltpD5DOkcmvhngk6jPjF5B6KhjNo4Askvv2nKnC/Re+ +t0pJTEBmLeDTmFGXQ+38PVeYz9bfOUrugZUhANZ0QQ5RyY8Be6pCOwoUB2cPJ93T +GgrEFtKcNBp1BZliLpfg/JI= +-----END CERTIFICATE REQUEST----- diff --git a/test/quic/http3/certs/san.conf b/test/quic/http3/certs/san.conf new file mode 100644 index 000000000..0ad6f5779 --- /dev/null +++ b/test/quic/http3/certs/san.conf @@ -0,0 +1,26 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext +x509_extensions = x509_ext + +[dn] +C = CN +ST = ShangHai +L = ChangNing +O = IQIYI +OU = IIG/QLB +emailAddress=yuwenchao@qiyi.com +CN = *.iqiyi.com + +[req_ext] +subjectAltName = @alt_names + +[x509_ext] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = *.iqiyi.com +DNS.2 = *.qiyi.domain diff --git a/test/quic/http3/h3client/h3client.go b/test/quic/http3/h3client/h3client.go new file mode 100644 index 000000000..246a23eb1 --- /dev/null +++ b/test/quic/http3/h3client/h3client.go @@ -0,0 +1,94 @@ +package main + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "flag" + "io" + "log" + "net/http" + "os" + "sync" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" + "github.com/quic-go/quic-go/qlog" +) + +func main() { + quiet := flag.Bool("q", false, "don't print the data") + keyLogFile := flag.String("keylog", "", "key log file") + insecure := flag.Bool("insecure", false, "skip certificate verification") + cert := flag.String("cert", "", "TLS certificate") + flag.Parse() + urls := flag.Args() + + var keyLog io.Writer + if len(*keyLogFile) > 0 { + f, err := os.Create(*keyLogFile) + if err != nil { + log.Fatal(err) + } + defer f.Close() + keyLog = f + } + + pool, err := x509.SystemCertPool() + if err != nil { + log.Fatal(err) + } + + if *cert != "" { + if _, err = os.Stat(*cert); err == nil { + caCertRaw, err := os.ReadFile(*cert) + if err != nil { + panic(err) + } + if ok := pool.AppendCertsFromPEM(caCertRaw); !ok { + panic("Could not add root certificate to pool.") + } + } + } + + roundTripper := &http3.RoundTripper{ + TLSClientConfig: &tls.Config{ + RootCAs: pool, + InsecureSkipVerify: *insecure, + KeyLogWriter: keyLog, + }, + QUICConfig: &quic.Config{ + Tracer: qlog.DefaultTracer, + }, + } + defer roundTripper.Close() + hclient := &http.Client{ + Transport: roundTripper, + } + + var wg sync.WaitGroup + wg.Add(len(urls)) + for _, addr := range urls { + log.Printf("GET %s", addr) + go func(addr string) { + rsp, err := hclient.Get(addr) + if err != nil { + log.Fatal(err) + } + log.Printf("Got response for %s: %#v", addr, rsp) + + body := &bytes.Buffer{} + _, err = io.Copy(body, rsp.Body) + if err != nil { + log.Fatal(err) + } + if *quiet { + log.Printf("Response Body: %d bytes", body.Len()) + } else { + log.Printf("Response Body (%d bytes):\n%s", body.Len(), body.Bytes()) + } + wg.Done() + }(addr) + } + wg.Wait() +} diff --git a/test/quic/http3/h3server/h3server.go b/test/quic/http3/h3server/h3server.go new file mode 100644 index 000000000..1fd729edc --- /dev/null +++ b/test/quic/http3/h3server/h3server.go @@ -0,0 +1,176 @@ +package main + +import ( + "crypto/md5" + "errors" + "flag" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "strconv" + "strings" + "sync" + + _ "net/http/pprof" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" + "github.com/quic-go/quic-go/qlog" +) + +type binds []string + +func (b binds) String() string { + return strings.Join(b, ",") +} + +func (b *binds) Set(v string) error { + *b = strings.Split(v, ",") + return nil +} + +// Size is needed by the /demo/upload handler to determine the size of the uploaded file +type Size interface { + Size() int64 +} + +// See https://en.wikipedia.org/wiki/Lehmer_random_number_generator +func generatePRData(l int) []byte { + res := make([]byte, l) + seed := uint64(1) + for i := 0; i < l; i++ { + seed = seed * 48271 % 2147483647 + res[i] = byte(seed) + } + return res +} + +func setupHandler(www string) http.Handler { + mux := http.NewServeMux() + + if len(www) > 0 { + mux.Handle("/", http.FileServer(http.Dir(www))) + } else { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("%#v\n", r) + const maxSize = 1 << 30 // 1 GB + num, err := strconv.ParseInt(strings.ReplaceAll(r.RequestURI, "/", ""), 10, 64) + if err != nil || num <= 0 || num > maxSize { + w.WriteHeader(400) + return + } + w.Write(generatePRData(int(num))) + }) + } + + mux.HandleFunc("/demo/tile", func(w http.ResponseWriter, r *http.Request) { + // Small 40x40 png + w.Write([]byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, + 0x01, 0x03, 0x00, 0x00, 0x00, 0xb6, 0x30, 0x2a, 0x2e, 0x00, 0x00, 0x00, + 0x03, 0x50, 0x4c, 0x54, 0x45, 0x5a, 0xc3, 0x5a, 0xad, 0x38, 0xaa, 0xdb, + 0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x18, + 0x61, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xe2, 0xb8, 0x75, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + }) + }) + + mux.HandleFunc("/demo/tiles", func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, "") + for i := 0; i < 200; i++ { + fmt.Fprintf(w, ``, i) + } + io.WriteString(w, "") + }) + + mux.HandleFunc("/demo/echo", func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + fmt.Printf("error reading body while handling /echo: %s\n", err.Error()) + } + w.Write(body) + }) + + // accept file uploads and return the MD5 of the uploaded file + // maximum accepted file size is 1 GB + mux.HandleFunc("/demo/upload", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + err := r.ParseMultipartForm(1 << 30) // 1 GB + if err == nil { + var file multipart.File + file, _, err = r.FormFile("uploadfile") + if err == nil { + var size int64 + if sizeInterface, ok := file.(Size); ok { + size = sizeInterface.Size() + b := make([]byte, size) + file.Read(b) + md5 := md5.Sum(b) + fmt.Fprintf(w, "%x", md5) + return + } + err = errors.New("couldn't get uploaded file size") + } + } + log.Printf("Error receiving upload: %#v", err) + } + io.WriteString(w, `
+
+ +
`) + }) + + return mux +} + +func main() { + // defer profile.Start().Stop() + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() + // runtime.SetBlockProfileRate(1) + + bs := binds{} + flag.Var(&bs, "bind", "bind to") + www := flag.String("www", "", "www data") + tcp := flag.Bool("tcp", false, "also listen on TCP") + key := flag.String("key", "./http3/certs/key.pem", "TLS key (requires -cert option)") + cert := flag.String("cert", "./http3/certs/cert.pem", "TLS certificate (requires -key option)") + flag.Parse() + + if len(bs) == 0 { + bs = binds{"localhost:6121"} + } + + handler := setupHandler(*www) + + var wg sync.WaitGroup + wg.Add(len(bs)) + for _, b := range bs { + fmt.Println("listening on", b) + bCap := b + go func() { + var err error + if *tcp { + err = http3.ListenAndServe(bCap, *cert, *key, handler) + } else { + server := http3.Server{ + Handler: handler, + Addr: bCap, + QUICConfig: &quic.Config{ + Tracer: qlog.DefaultTracer, + }, + } + err = server.ListenAndServeTLS(*cert, *key) + } + if err != nil { + fmt.Println(err) + } + wg.Done() + }() + } + wg.Wait() +} diff --git a/test/quic/pkg/cid/cid_generator.go b/test/quic/pkg/cid/cid_generator.go new file mode 100644 index 000000000..90307410c --- /dev/null +++ b/test/quic/pkg/cid/cid_generator.go @@ -0,0 +1,131 @@ +package cid + +import ( + "crypto/rand" + "fmt" + "io" + "net" + + quic "github.com/quic-go/quic-go" +) + +const ( + QUIC_CID_BUF_LEN = 20 + DPVS_QUIC_DCID_BYTES_MIN = 7 +) + +type DpvsQCID struct { + cidLen uint8 + l3len uint8 + l4len uint8 + svrIP net.IP + svrPort uint16 +} + +var _ quic.ConnectionIDGenerator = (*DpvsQCID)(nil) + +func NewDpvsQCID(cidLen, l3len, l4len uint8, + svrIP net.IP, svrPort uint16) *DpvsQCID { + if cidLen < DPVS_QUIC_DCID_BYTES_MIN { + cidLen = DPVS_QUIC_DCID_BYTES_MIN + } + if l3len < 1 { + l3len = 1 + } else if l3len > 8 { + l3len = 8 + } + if l4len > 0 { + l4len = 2 + } + if svrIP == nil { + svrIP, _ = FindLocalIP("") + } + + return &DpvsQCID{ + cidLen: cidLen, + l3len: l3len, + l4len: l4len, + svrIP: svrIP, + svrPort: svrPort, + } +} + +func (dqcid *DpvsQCID) ConnectionIDLen() int { + return int(dqcid.cidLen) +} + +func (dqcid *DpvsQCID) GenerateConnectionID() (quic.ConnectionID, error) { + data, err := QuicCIDGeneratorFunction(dqcid.cidLen, dqcid.l3len, + dqcid.l4len, dqcid.svrIP, dqcid.svrPort) + if err != nil { + data = make([]byte, dqcid.cidLen) + rand.Read(data[:]) + } + return quic.ConnectionIDFromBytes(data), err +} + +func QuicCIDGeneratorFunction( + cidLen uint8, // the total length of CID to be generated, 7~20 bytes + l3len uint8, // the length of server IP to encode into CID, 1~8 bytes + l4len uint8, // the length of server Port to encode into CID, 0 or 2 bytes + svrIP net.IP, // the server IP + svrPort uint16, // the server Port +) ([]byte, error) { + rdbuf := make([]byte, QUIC_CID_BUF_LEN) + var i uint8 + var l3addr []byte + var l4addr uint16 + + if svrIP == nil || + cidLen < DPVS_QUIC_DCID_BYTES_MIN || + l3len > 8 || l3len < 1 || + (l4len != 0 && l4len != 2) || + cidLen < l3len+l4len+5 { + return nil, fmt.Errorf("invalid params") + } + + entropy := cidLen - l3len - l4len + 1 + l4flag := 0 + if l4len > 0 { + l4flag = 1 + } + + ipbytes := svrIP.To4() + if ipbytes != nil { + l3addr = ipbytes[4-l3len:] + } else { + ipbytes = svrIP.To16() + if ipbytes == nil { + return nil, fmt.Errorf("invalid IP %v", svrIP) + } + l3addr = ipbytes[16-l3len:] + } + l4addr = svrPort + + if _, err := io.ReadFull(rand.Reader, rdbuf[:entropy]); err != nil { + return nil, err + } + + cid := make([]byte, cidLen, cidLen) + cid[0] = rdbuf[0] + cid[1] = uint8(((l3len-1)&0x7)<<5) | uint8((l4flag&0x1)<<4) | ((uint8(l3addr[0]) >> 4) & 0xf) + for i = 0; i < l3len; i++ { + if i == l3len-1 { + cid[2+i] = ((l3addr[0] & 0xf) << 4) + } else { + cid[2+i] = ((l3addr[0] & 0xf) << 4) | ((l3addr[1] >> 4) & 0xf) + } + l3addr = l3addr[1:] + } + if l4len > 0 { + cid[l3len+1] &= 0xf0 + cid[l3len+1] |= byte((l4addr >> 12) & 0xf) + l4addr <<= 4 + cid[l3len+2] = byte((l4addr >> 8) & 0xff) + cid[l3len+3] = byte(l4addr & 0xff) + } + cid[l3len+l4len+1] |= (rdbuf[1] & 0xf) + copy(cid[l3len+l4len+2:], rdbuf[2:entropy-1]) + + return cid, nil +} diff --git a/test/quic/pkg/cid/cid_generator_test.go b/test/quic/pkg/cid/cid_generator_test.go new file mode 100644 index 000000000..652606ed5 --- /dev/null +++ b/test/quic/pkg/cid/cid_generator_test.go @@ -0,0 +1,47 @@ +package cid + +import ( + "net" + "reflect" + "testing" +) + +func TestQuicCIDGenerator_IPv4(t *testing.T) { + cid, err := QuicCIDGeneratorFunction(10, 3, 2, net.ParseIP("192.168.111.222"), 8029) + if err != nil { + t.Errorf("QuicCIDGenerator error return: %v", err) + } + if len(cid) != 10 { + t.Errorf("invalid CID length") + } + result := make([]byte, 6) + copy(result, cid[1:7]) + result[len(result)-1] &= 0xf0 + expected := []byte{0x5a, 0x86, 0xfd, 0xe1, 0xf5, 0xd0} + + if !reflect.DeepEqual(result, expected) { + t.Errorf("mismatched CID:\nresult: %x\nexpect: %x\n", result, expected) + } else { + t.Logf("%x\n", cid) + } +} + +func TestQuicCIDGenerator_IPv6(t *testing.T) { + cid, err := QuicCIDGeneratorFunction(16, 6, 2, + net.ParseIP("2001::123:4567:89ab:cdef"), 51321) + if err != nil { + t.Errorf("QuicCIDGenerator error return: %v", err) + } + if len(cid) != 16 { + t.Errorf("invalid CID length") + } + result := make([]byte, 9) + copy(result, cid[1:10]) + result[len(result)-1] &= 0xf0 + expected := []byte{0xb4, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xfc, 0x87, 0x90} + if !reflect.DeepEqual(result, expected) { + t.Errorf("mismatched CID:\nresult: %x\nexpect: %x\n", result, expected) + } else { + t.Logf("%x\n", cid) + } +} diff --git a/test/quic/pkg/cid/server_addr.go b/test/quic/pkg/cid/server_addr.go new file mode 100644 index 000000000..b1aab217a --- /dev/null +++ b/test/quic/pkg/cid/server_addr.go @@ -0,0 +1,25 @@ +package cid + +import ( + "net" +) + +func FindLocalIP(targetIP string) (net.IP, error) { + if len(targetIP) == 0 { + targetIP = "8.8.8.8" + } + + raddr, err := net.ResolveIPAddr("ip", targetIP) + if err != nil { + return nil, err + } + + conn, err := net.DialIP("ip:icmp", nil, raddr) + if err != nil { + return nil, err + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.IPAddr) + return localAddr.IP, nil +} diff --git a/test/quic/pkg/uoa/uoa.go b/test/quic/pkg/uoa/uoa.go new file mode 100644 index 000000000..5485aeae2 --- /dev/null +++ b/test/quic/pkg/uoa/uoa.go @@ -0,0 +1,120 @@ +package uoa + +import ( + "encoding/binary" + "fmt" + "net" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + IPOPT_CONTROL = 0 + IPOPT_UOA = 31 | IPOPT_CONTROL + UOA_SO_GET_LOOKUP = 2048 + + AF_INET = 2 + AF_INET6 = 10 +) + +type uoaParamMap struct { + // input + af uint16 + saddr [16]byte + daddr [16]byte + sport uint16 + dport uint16 + + // output + realAf uint16 + realSaddr [16]byte + realSport uint16 +} + +func AddrToIPnPort(addr net.Addr) (net.IP, uint16, error) { + switch t := addr.(type) { + case *net.TCPAddr: + return t.IP, uint16(t.Port), nil + case *net.UDPAddr: + return t.IP, uint16(t.Port), nil + default: + return nil, 0, fmt.Errorf("unsupported address type %T for %s", t, addr) + } +} + +func IPnPortToAddr(af uint16, l4Proto string, addr [16]byte, port uint16) (net.Addr, error) { + // fmt.Println("uoa address", af, l4Proto, addr, port) + switch l4Proto { + case "tcp": + res := &net.TCPAddr{} + if af == AF_INET { + res.IP = net.IPv4(addr[0], addr[1], addr[2], addr[3]) + } else { + res.IP = make(net.IP, net.IPv6len) + copy(res.IP, addr[:]) + } + res.Port = int(port) + return res, nil + case "udp": + res := &net.UDPAddr{} + if af == AF_INET { + res.IP = net.IPv4(addr[0], addr[1], addr[2], addr[3]) + } else { + res.IP = make(net.IP, net.IPv6len) + copy(res.IP, addr[:]) + } + res.Port = int(port) + return res, nil + default: + return nil, fmt.Errorf("unsupported network type %q", l4Proto) + } +} + +func htons(le uint16) uint16 { + bytes := make([]byte, 2) + binary.LittleEndian.PutUint16(bytes, le) + return binary.BigEndian.Uint16(bytes) +} + +func ntohs(be uint16) uint16 { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, be) + return binary.LittleEndian.Uint16(bytes) +} + +func GetUoaAddr(fd uintptr, saddr, daddr net.Addr) (net.Addr, error) { + sip, sport, err := AddrToIPnPort(saddr) + if err != nil { + return nil, err + } + _, dport, err := AddrToIPnPort(daddr) // server ip doesn't matter + if err != nil { + return nil, err + } + uoaParam := uoaParamMap{} + if sip.To4() != nil { + uoaParam.af = AF_INET + copy(uoaParam.saddr[:], sip.To4()) + //copy(uoaParam.daddr[:], dip.To4()) + } else { + uoaParam.af = AF_INET6 + copy(uoaParam.saddr[:], sip.To16()) + //copy(uoaParam.daddr[:], dip.To16()) + } + uoaParam.sport = htons(sport) + uoaParam.dport = htons(dport) + paramLen := uint32(unsafe.Sizeof(uoaParam)) + //fmt.Println(fd, uoaParam, paramLen) + _, _, errno := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, unix.IPPROTO_IP, UOA_SO_GET_LOOKUP, uintptr(unsafe.Pointer(&uoaParam)), uintptr(unsafe.Pointer(¶mLen)), 0) + if errno != 0 { + return nil, fmt.Errorf("syscall failed with errno %d", errno) + } + + res, err := IPnPortToAddr(uoaParam.realAf, "udp", uoaParam.realSaddr, ntohs(uoaParam.realSport)) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/test/quic/server/quic-server.go b/test/quic/server/quic-server.go new file mode 100644 index 000000000..690f044c6 --- /dev/null +++ b/test/quic/server/quic-server.go @@ -0,0 +1,167 @@ +package main + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "flag" + "fmt" + "log" + "math/big" + "net" + "os" + "syscall" + + quic "github.com/quic-go/quic-go" + + "quic-test/pkg/cid" + "quic-test/pkg/uoa" +) + +var ( + hostIP net.IP +) + +func init() { + if hostIP, _ = cid.FindLocalIP(""); hostIP == nil { + hostIP = net.IPv4(127, 0, 0, 1) + } + fmt.Println("Host IP:", hostIP) +} + +func main() { + servAddr := flag.String("server", ":4242", "server listener address") + keyLogFile := flag.String("keylog", "", "key log file") + uoaCliAddr := flag.Bool("uoa", true, "enable uoa client address") + flag.Parse() + + fmt.Printf("Quic Server listens on %s (uoa client address %v)\n", *servAddr, *uoaCliAddr) + + tlsConf := generateTLSConfig() + if *keyLogFile != "" { + keyLog, err := os.Create(*keyLogFile) + if err != nil { + log.Fatal(err) + } + defer keyLog.Close() + tlsConf.KeyLogWriter = keyLog + } + + cidGenerator := cid.NewDpvsQCID(10, 4, 0, hostIP, 0) + + /* + listener, err := quic.ListenAddr(*servAddr, tlsConf, nil) + if err != nil { + panic(err) + } + */ + udpAddr, err := net.ResolveUDPAddr("udp", *servAddr) + if err != nil { + panic(err) + } + udpConn, err := net.ListenUDP("udp", udpAddr) + if err != nil { + panic(err) + } + listener, err := (&quic.Transport{ + Conn: udpConn, + ConnectionIDGenerator: cidGenerator, + }).Listen(tlsConf, nil) + if err != nil { + panic(err) + } + defer listener.Close() + + var uoaConn *net.UDPConn + if *uoaCliAddr { + uoaConn = udpConn + } + + ctx := context.Background() + for { + sess, err := listener.Accept(ctx) + if err != nil { + panic(err) + } + go handleSession(ctx, uoaConn, sess) + } +} + +func handleSession(ctx context.Context, udpConn *net.UDPConn, sess quic.Connection) { + if udpConn != nil { + file, err := udpConn.File() + if err != nil { + panic(err) + } + defer file.Close() + + // FIXME: Even though the file is an duplicate from the original udpConn. + // a just single call to file.Fd() blocks the quic session noticeably when + // using the default blocking mode. Having no idea about the cause of this + // problem, just set the fd to be nonblock, hoping without other influences. + fd := file.Fd() + syscall.SetNonblock(int(fd), true) + + uoaAddr, err := uoa.GetUoaAddr(fd, sess.RemoteAddr(), sess.LocalAddr()) + if err != nil { + fmt.Printf("New connection from %v, uoaAddr failed for %v\n", sess.RemoteAddr(), err) + } else { + fmt.Printf("New connection from %v, uoaAddr %v\n", sess.RemoteAddr(), uoaAddr) + } + } else { + fmt.Printf("New connection from %v\n", sess.RemoteAddr()) + } + + stream, err := sess.AcceptStream(ctx) + if err != nil { + panic(err) + } + defer stream.Close() + + fmt.Printf("accepted new conn stream: %v\n", stream.StreamID()) + + // the server simply echo the received data back to client + buffer := make([]byte, 32) + _, err = stream.Read(buffer) + if err != nil { + panic(err) + } + fmt.Printf("got data: %s\n", buffer) + + buffer = []byte("Hello, QUIC Client!") + _, err = stream.Write(buffer) + if err != nil { + panic(err) + } + fmt.Printf("sent data: %s\n", buffer) +} + +// generateTLSConfig creates TLS configs required for TLS handshake. +// In fact, you should use an authorized certificate released from CA. +func generateTLSConfig() *tls.Config { + key, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + panic(err) + } + template := x509.Certificate{SerialNumber: big.NewInt(1)} + template.Subject = pkix.Name{Organization: []string{"quic-go"}} + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + panic(err) + } + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + + tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + panic(err) + } + return &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + NextProtos: []string{"quic-echo-example"}, + } +} diff --git a/tools/dpip/Makefile b/tools/dpip/Makefile index e1bbe21e4..3ed2aaf15 100644 --- a/tools/dpip/Makefile +++ b/tools/dpip/Makefile @@ -39,8 +39,8 @@ DEFS = -D DPVS_MAX_LCORE=64 -D DPIP_VERSION=\"$(VERSION_STRING)\" CFLAGS += $(DEFS) -OBJS = ipset.o dpip.o utils.o route.o addr.o neigh.o link.o vlan.o \ - qsch.o cls.o tunnel.o ipset.o ipv6.o iftraf.o eal_mem.o flow.o \ +OBJS = ipset.o dpip.o utils.o route.o addr.o neigh.o link.o vlan.o maddr.o \ + qsch.o cls.o tunnel.o ipset.o ipv6.o iftraf.o eal_mem.o flow.o lldp.o \ ../../src/common.o ../keepalived/keepalived/check/sockopt.o all: $(TARGET) diff --git a/tools/dpip/cls.c b/tools/dpip/cls.c index cab722a68..f38d7e63f 100644 --- a/tools/dpip/cls.c +++ b/tools/dpip/cls.c @@ -49,7 +49,7 @@ static void cls_help(void) " PATTERN := comma seperated of tokens below,\n" " { PROTO | SRANGE | DRANGE | IIF | OIF }\n" " CHILD_QSCH := child qsch handle of the qsch cls attached.\n" - " PROTO := \"{ tcp | udp }\"\n" + " PROTO := \"{ tcp | sctp | udp }\"\n" " SRANGE := \"from=RANGE\"\n" " DRANGE := \"to=RANGE\"\n" " RANGE := ADDR[-ADDR][:PORT[-PORT]]\n" diff --git a/tools/dpip/dpip.c b/tools/dpip/dpip.c index 596a534f5..1249e0b3b 100644 --- a/tools/dpip/dpip.c +++ b/tools/dpip/dpip.c @@ -35,7 +35,7 @@ static void usage(void) " "DPIP_NAME" [OPTIONS] OBJECT { COMMAND | help }\n" "Parameters:\n" " OBJECT := { link | addr | route | neigh | vlan | tunnel | qsch | cls |\n" - " ipv6 | iftraf | eal-mem | ipset | flow }\n" + " ipv6 | iftraf | eal-mem | ipset | flow | maddr | lldp }\n" " COMMAND := { create | destroy | add | del | show (list) | set (change) |\n" " replace | flush | test | enable | disable }\n" "Options:\n" diff --git a/tools/dpip/ipset.c b/tools/dpip/ipset.c index baeee9257..892b2374f 100644 --- a/tools/dpip/ipset.c +++ b/tools/dpip/ipset.c @@ -125,7 +125,7 @@ ipset_help(void) " MAC := 6 bytes MAC address string literal\n" " PORT := \"[{ tcp | udp | icmp | icmp6 }:]port1[-port2]\"\n" " OPTIONS := { comment | range NET | hashsize NUM | maxelem NUM }\n" - " ADTOPTS := { comment STRING | unmatch (for add only) }\n" + " ADTOPTS := { comment STRING | nomatch (for add only) }\n" " flag := { -F(--force) | { -4 | -6 } | -v }\n" "Examples:\n" " dpip ipset create foo bitmap:ip range 192.168.0.0/16 comment\n" @@ -147,7 +147,7 @@ static int addr_arg_parse(char *arg, struct inet_addr_range *range, uint8_t *cidr) { char *ip1, *ip2, *sep; - int *af = ¶m.option.family; + uint8_t *af = ¶m.option.family; /* ip/cidr */ if (cidr && (sep = strstr(arg, "/"))) { @@ -1081,7 +1081,11 @@ ipset_info_dump(struct ipset_info *info, bool sort) struct ipset_member *member; char header[HEADER_LEN], *members; - type = get_type_idx(); + type = get_type_idx_from_type(info->type); + if (type < 0) { + fprintf(stderr, "unsupported ipset type %s\n", info->type); + return; + } /* header */ types[type].dump_header(header, info); @@ -1099,7 +1103,7 @@ ipset_info_dump(struct ipset_info *info, bool sort) struct ipset_member *members = (struct ipset_member*)info->members; sort_compare_func sort_compare = types[type].sort_compare; - for (i = 0; i < info->entries - 1; i++) { + for (i = 0; i + 1 < info->entries; i++) { min = i; for (j = i + 1; j < info->entries; j++) { if (sort_compare(info->af, &members[min], &members[j]) > 0) diff --git a/tools/dpip/link.c b/tools/dpip/link.c index f2d3f7798..acaab26b5 100644 --- a/tools/dpip/link.c +++ b/tools/dpip/link.c @@ -101,8 +101,10 @@ static inline int get_netif_port_list(void) if (g_nic_list) free(g_nic_list); g_nic_list = calloc(1, len); - if (!g_nic_list) + if (!g_nic_list) { + dpvs_sockopt_msg_free((void *)p_port_list); return EDPVS_NOMEM; + } memcpy(g_nic_list, p_port_list, len); @@ -125,7 +127,7 @@ static void link_help(void) " dpip link set DEV-NAME ITEM VALUE\n" " ---supported items---\n" - " promisc [on|off], forward2kni [on|off], link [up|down],\n" + " promisc [on|off], forward2kni [on|off], link [up|down], lldp [up|down]\n" " allmulticast [on|off], tc-egress [on|off], tc-ingress [on|off], addr, \n" " bond-[mode|slave|primary|xmit-policy|monitor-interval|link-up-prop|" "link-down-prop]\n" @@ -258,6 +260,9 @@ static int dump_nic_basic(char *name, int namelen) if (get.tc_ingress) printf("tc-ingress "); + if (get.lldp) + printf("lldp "); + printf("\n"); printf(" addr %s ", get.addr); @@ -501,6 +506,7 @@ static int dump_bond_status(char *name, int namelen) p_get->link_up_prop_delay); if (p_get->slave_nb > NETIF_MAX_BOND_SLAVES) { printf("too many slaves: %d\n", p_get->slave_nb); + dpvs_sockopt_msg_free(p_get); return EDPVS_INVAL; } for (i = 0; i < p_get->slave_nb; i++) { @@ -514,6 +520,7 @@ static int dump_bond_status(char *name, int namelen) } printf("\n"); + dpvs_sockopt_msg_free(p_get); return EDPVS_OK; } @@ -954,6 +961,25 @@ static int link_nic_set_tc_ingress(const char *name, const char *value) return dpvs_setsockopt(SOCKOPT_NETIF_SET_PORT, &cfg, sizeof(netif_nic_set_t)); } +static int link_nic_set_lldp(const char *name, const char *value) +{ + netif_nic_set_t cfg = {}; + assert(value); + + snprintf(cfg.pname, sizeof(cfg.pname), "%s", name); + + if (strcmp(value, "on") == 0) + cfg.lldp_on = 1; + else if(strcmp(value, "off") == 0) + cfg.lldp_off = 1; + else { + fprintf(stderr, "invalid arguement value for 'lldp'\n"); + return EDPVS_INVAL; + } + + return dpvs_setsockopt(SOCKOPT_NETIF_SET_PORT, &cfg, sizeof(netif_nic_set_t)); +} + static int link_bond_add_bond_slave(const char *name, const char *value) { netif_bond_set_t cfg; @@ -1189,6 +1215,8 @@ static int link_set(struct link_param *param) link_nic_set_tc_egress(param->dev_name, param->value); else if (strcmp(param->item, "tc-ingress") == 0) link_nic_set_tc_ingress(param->dev_name, param->value); + else if (strcmp(param->item, "lldp") == 0) + link_nic_set_lldp(param->dev_name, param->value); else { fprintf(stderr, "invalid parameter name '%s'\n", param->item); return EDPVS_INVAL; diff --git a/tools/dpip/lldp.c b/tools/dpip/lldp.c new file mode 100644 index 000000000..aed52c91c --- /dev/null +++ b/tools/dpip/lldp.c @@ -0,0 +1,128 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#include +#include "dpip.h" +#include "sockopt.h" +#include "conf/lldp.h" + +static void lldp_help(void) +{ + fprintf(stderr, + "Usage:\n" + " dpip lldp show TYPE dev NAME\n" + " TYPE := [ local | neigh ]\n" + " NAME := interface name\n" + "Examples:\n" + " dpip lldp show local dev dpdk0\n" + " dpip lldp show dev dpdk1 neigh\n"); +} + +static int lldp_parse(struct dpip_obj *obj, struct dpip_conf *conf) +{ + struct lldp_param *param = obj->param; + + memset(param, 0, sizeof(*param)); + + while (conf->argc > 0) { + if (strcmp(conf->argv[0], "dev") == 0) { + NEXTARG_CHECK(conf, conf->argv[0]); + snprintf(param->ifname, sizeof(param->ifname), "%s", conf->argv[0]); + } else { + if (strcmp(conf->argv[0], "local") == 0) { + param->node = DPVS_LLDP_NODE_LOCAL; + } else if (strcmp(conf->argv[0], "neigh") == 0) { + param->node = DPVS_LLDP_NODE_NEIGH; + } else { + fprintf(stderr, "too many arguments\n"); + return EDPVS_INVAL; + } + } + NEXTARG(conf); + } + + return EDPVS_OK; +} + +static int lldp_check(const struct dpip_obj *obj, dpip_cmd_t cmd) +{ + const struct lldp_param *param = obj->param; + + /* sanity check */ + switch (cmd) { + case DPIP_CMD_SHOW: + if (strlen(param->ifname) == 0) { + fprintf(stderr, "missing device name\n"); + return EDPVS_INVAL; + } + return EDPVS_OK; + default: + return EDPVS_NOTSUPP; + } + return EDPVS_OK; +} + +static int lldp_do_cmd(struct dpip_obj *obj, dpip_cmd_t cmd, struct dpip_conf *conf) +{ + const struct lldp_param *param = obj->param; + struct lldp_message *message; + size_t size; + int err; + + switch (cmd) { + case DPIP_CMD_SHOW: + err = dpvs_getsockopt(SOCKOPT_GET_LLDP_SHOW, param, sizeof(*param), + (void **)&message, &size); + if (err != EDPVS_OK) + return err; + + if (size < sizeof(*message)) { + fprintf(stderr, "corrupted response\n"); + dpvs_sockopt_msg_free(message); + return EDPVS_INVAL; + } + printf("-*-*-*- %s LLDP Message on Port %s -*-*-*-\n", + message->param.node == DPVS_LLDP_NODE_NEIGH ? "Neighbour" : "Local", + message->param.ifname); + printf(message->message); + dpvs_sockopt_msg_free(message); + return EDPVS_OK; + default: + return EDPVS_NOTSUPP; + } +} + +static struct lldp_param lldp_param; + +static struct dpip_obj dpip_lldp = { + .name = "lldp", + .param = &lldp_param, + .help = lldp_help, + .parse = lldp_parse, + .check = lldp_check, + .do_cmd = lldp_do_cmd, +}; + +static void __init lldp_init(void) +{ + dpip_register_obj(&dpip_lldp); +} + +static void __exit lldp_exit(void) +{ + dpip_unregister_obj(&dpip_lldp); +} diff --git a/tools/dpip/maddr.c b/tools/dpip/maddr.c new file mode 100644 index 000000000..7bb82a544 --- /dev/null +++ b/tools/dpip/maddr.c @@ -0,0 +1,168 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ +#include "dpip.h" +#include "conf/sockopts.h" +#include "conf/common.h" +#include "conf/netif.h" +#include "conf/netif_addr.h" +#include "conf/inetaddr.h" +#include "sockopt.h" + +static void maddr_help(void) +{ + fprintf(stderr, "Usage: dpip maddr show [dev STRING]\n"); +} + +static int maddr_parse_args(struct dpip_conf *conf, char *ifname, size_t len) +{ + while (conf->argc > 0) { + if (strcmp(conf->argv[0], "dev") == 0) { + NEXTARG_CHECK(conf, "dev"); + snprintf(ifname, len, "%s", conf->argv[0]); + } + NEXTARG(conf); + } + + if (conf->argc > 0) { + fprintf(stderr, "too many arguments\n"); + return -1; + } + + return 0; +} + +static int hwm_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int i, err; + size_t outlen; + struct netif_hw_addr_array *out; + struct netif_hw_addr_entry *entry; + + err = dpvs_getsockopt(SOCKOPT_NETIF_GET_MADDR, ifname, len, (void **)&out, &outlen); + if (err != EDPVS_OK || !out || !outlen) + return err; + + for (i = 0; i < out->count; i++) { + entry = &out->entries[i]; + if (verbose) { + printf("\tlink %s%s\t\trefcnt %u\t\tsync %d\n", + entry->addr, entry->flags & HW_ADDR_F_FROM_KNI ? " (+kni)" : "", + entry->refcnt, entry->sync_cnt); + } else { + printf("\tlink %s\n", entry->addr); + } + } + + dpvs_sockopt_msg_free(out); + return EDPVS_OK; +} + +static int ifm_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int i, err; + size_t outlen; + struct inet_maddr_array *out; + struct inet_maddr_entry *entry; + char ipbuf[64]; + + err = dpvs_getsockopt(SOCKOPT_GET_IFMADDR_SHOW, ifname, len, (void **)&out, &outlen); + if (err != EDPVS_OK || !out || !outlen) + return err; + + for (i = 0; i < out->nmaddr; i++) { + entry = &out->maddrs[i]; + if (verbose) { + printf("\t%5s %s\t\tflags 0x%x\t\trefcnt %u\n", entry->af == AF_INET6 ? "inet6" : "inet", + inet_ntop(entry->af, &entry->maddr, ipbuf, sizeof(ipbuf)) ? ipbuf : "unknown", + entry->flags, entry->refcnt); + } else { + printf("\t%5s %s\n", entry->af == AF_INET6 ? "inet6" : "inet", + inet_ntop(entry->af, &entry->maddr, ipbuf, sizeof(ipbuf)) ? ipbuf : "unknown"); + } + } + + dpvs_sockopt_msg_free(out); + return EDPVS_OK; +} + +static int maddr_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int err; + + err = hwm_get_and_dump(ifname, len, verbose); + if (err != EDPVS_OK) + return err; + + return ifm_get_and_dump(ifname, len, verbose); +} + +static int maddr_do_cmd(struct dpip_obj *obj, dpip_cmd_t cmd, + struct dpip_conf *conf) +{ + int i, err; + size_t len; + char ifname[IFNAMSIZ] = { 0 }; + netif_nic_list_get_t *ports; + + if (maddr_parse_args(conf, ifname, sizeof(ifname)) != 0) + return EDPVS_INVAL; + + switch (conf->cmd) { + case DPIP_CMD_SHOW: + if (strlen(ifname) > 0) { + printf("%s:\n", ifname); + return maddr_get_and_dump(ifname, sizeof(ifname), conf->verbose); + } + + /* list all devices */ + err = dpvs_getsockopt(SOCKOPT_NETIF_GET_PORT_LIST, NULL, 0, (void **)&ports, &len); + if (err != EDPVS_OK || !ports || !len) + return err; + for (i = 0; i < ports->nic_num && i < NETIF_MAX_PORTS; i++) { + printf("%d:\t%s\n", ports->idname[i].id + 1, ports->idname[i].name); + err = maddr_get_and_dump(ports->idname[i].name, + sizeof(ports->idname[i].name), conf->verbose); + if (err != EDPVS_OK) { + dpvs_sockopt_msg_free(ports); + return err; + } + } + dpvs_sockopt_msg_free(ports); + break; + default: + return EDPVS_NOTSUPP; + } + + return EDPVS_OK; +} + +struct dpip_obj dpip_maddr = { + .name = "maddr", + .help = maddr_help, + .do_cmd = maddr_do_cmd, +}; + +static void __init maddr_init(void) +{ + dpip_register_obj(&dpip_maddr); +} + +static void __exit maddr_exit(void) +{ + dpip_unregister_obj(&dpip_maddr); +} diff --git a/tools/dpvs-agent/cmd/device/put_device_name_addr.go b/tools/dpvs-agent/cmd/device/put_device_name_addr.go index 275e33100..4dff0e3eb 100644 --- a/tools/dpvs-agent/cmd/device/put_device_name_addr.go +++ b/tools/dpvs-agent/cmd/device/put_device_name_addr.go @@ -17,6 +17,7 @@ package device import ( "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiDevice "github.com/dpvs-agent/restapi/operations/device" @@ -52,6 +53,12 @@ func (h *putDeviceAddr) Handle(params apiDevice.PutDeviceNameAddrParams) middlew if params.Sapool != nil && *params.Sapool { addr.SetFlags("sapool") } + + if params.Snapshot != nil && *params.Snapshot { + AnnouncePort := settings.ShareSnapshot().NodeSpec.AnnouncePort + AnnouncePort.Dpvs = params.Name + } + // addr.SetValidLft(prarms.Spec.ValidLft) // addr.SetPreferedLft(prarms.Spec.ValidLft) diff --git a/tools/dpvs-agent/cmd/device/put_device_name_netlink_addr.go b/tools/dpvs-agent/cmd/device/put_device_name_netlink_addr.go index 1b093b431..c03495990 100644 --- a/tools/dpvs-agent/cmd/device/put_device_name_netlink_addr.go +++ b/tools/dpvs-agent/cmd/device/put_device_name_netlink_addr.go @@ -15,6 +15,7 @@ package device import ( + "errors" "fmt" "net" "strings" @@ -22,6 +23,7 @@ import ( "github.com/vishvananda/netlink" "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/settings" apiDevice "github.com/dpvs-agent/restapi/operations/device" "github.com/go-openapi/runtime/middleware" @@ -44,43 +46,54 @@ func NewPutDeviceNetlinkAddr(cp *pool.ConnPool, parentLogger hclog.Logger) *putD // ip addr add 10.0.0.1/32 dev eth0 func (h *putDeviceNetlinkAddr) Handle(params apiDevice.PutDeviceNameNetlinkAddrParams) middleware.Responder { // h.logger.Info("/v2/device/", params.Name, "/netlink/addr ", params.Spec.Addr) + if err := NetlinkAddrAdd(params.Spec.Addr, params.Name, h.logger); err != nil { + return apiDevice.NewPutDeviceNameNetlinkAddrInternalServerError() + } + if params.Snapshot != nil && *params.Snapshot { + AnnouncePort := settings.ShareSnapshot().NodeSpec.AnnouncePort + AnnouncePort.Switch = params.Name + } + return apiDevice.NewPutDeviceNameNetlinkAddrOK() +} + +func NetlinkAddrAdd(addr, device string, logger hclog.Logger) error { var cidr string - if strings.Count(params.Spec.Addr, "/") == 0 { - ip := net.ParseIP(params.Spec.Addr) + if strings.Count(addr, "/") == 0 { + ip := net.ParseIP(addr) if ip == nil { - h.logger.Info("Parse IP failed.", "Addr", params.Spec.Addr) - return apiDevice.NewPutDeviceNameNetlinkAddrInternalServerError() + logger.Info("Parse IP failed.", "Addr", addr) + return errors.New("Parse IP Failed.") } if ip.To4() != nil { - cidr = params.Spec.Addr + "/32" + cidr = addr + "/32" } else { - cidr = params.Spec.Addr + "/128" + cidr = addr + "/128" } } else { - cidr = params.Spec.Addr + cidr = addr } ip, ipnet, err := net.ParseCIDR(cidr) if err != nil { - h.logger.Error("Parse CIDR failed.", "cidr", cidr, "Error", err.Error()) - return apiDevice.NewPutDeviceNameNetlinkAddrInternalServerError() + logger.Error("Parse CIDR failed.", "cidr", cidr, "Error", err.Error()) + return err } ipnet.IP = ip - addr := &netlink.Addr{IPNet: ipnet} + netlinkAddr := &netlink.Addr{IPNet: ipnet} - link, err := netlink.LinkByName(params.Name) + link, err := netlink.LinkByName(device) if err != nil { - h.logger.Error("netlink.LinkByName() failed.", "Device Name", params.Name, "Error", err.Error()) - return apiDevice.NewPutDeviceNameNetlinkAddrInternalServerError() + logger.Error("netlink.LinkByName() failed.", "device", device, "Error", err.Error()) + return err } - if err := netlink.AddrAdd(link, addr); err != nil { - h.logger.Error("netlink.AddrAdd() failed.", "Error", err.Error()) - return apiDevice.NewPutDeviceNameNetlinkAddrInternalServerError() + if err := netlink.AddrAdd(link, netlinkAddr); err != nil { + logger.Error("netlink.AddrAdd() failed.", "Error", err.Error()) + return err } - cmd := fmt.Sprintf("ip addr add %s dev %s", cidr, params.Name) - h.logger.Info("Device add Addr success.", "cmd", cmd) - return apiDevice.NewPutDeviceNameNetlinkAddrOK() + cmd := fmt.Sprintf("ip addr add %s dev %s", cidr, device) + logger.Info("Device add Addr success.", "cmd", cmd) + return nil } diff --git a/tools/dpvs-agent/cmd/dpvs-agent-server/Makefile b/tools/dpvs-agent/cmd/dpvs-agent-server/Makefile index 84f6c3105..e5cd00eef 100644 --- a/tools/dpvs-agent/cmd/dpvs-agent-server/Makefile +++ b/tools/dpvs-agent/cmd/dpvs-agent-server/Makefile @@ -1,7 +1,7 @@ TARGET := dpvs-agent GO ?= go -LD_FLAGS = -ldflags="-s -w" +LD_FLAGS = -ldflags="-s -w -X main.buildVersion=$(git rev-parse --short HEAD)" GO_BUILD = CGO_ENABLED=0 $(GO) build $(LD_FLAGS) GO_CLEAN = $(GO) clean INSTALL = install diff --git a/tools/dpvs-agent/cmd/dpvs-agent-server/api_init.go b/tools/dpvs-agent/cmd/dpvs-agent-server/api_init.go index bc269e4ff..9fbb13937 100644 --- a/tools/dpvs-agent/cmd/dpvs-agent-server/api_init.go +++ b/tools/dpvs-agent/cmd/dpvs-agent-server/api_init.go @@ -17,17 +17,21 @@ package main import ( "context" "errors" + "fmt" "net" "os" + "path/filepath" "strings" "time" "github.com/hashicorp/go-hclog" - "github.com/lestrrat-go/file-rotatelogs" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/dpvs-agent/cmd/device" + "github.com/dpvs-agent/cmd/ipset" "github.com/dpvs-agent/cmd/ipvs" "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/settings" "github.com/dpvs-agent/restapi" "github.com/dpvs-agent/restapi/operations" ) @@ -37,7 +41,9 @@ var ( ) type DpvsAgentServer struct { + InitMode string `long:"init-mode" description:"load service from network or local config file. the options is [network|local]" default:"network"` LogDir string `long:"log-dir" description:"default log dir is /var/log/ And log name dpvs-agent.log" default:"/var/log/"` + CacheFile string `long:"cache-file" description:"a file path which used to dump the running dpvs active virtual service. we can load it while init by *local* mode and resume dpvs enviroment. if the file path is not specified, there is named with 'dpvs.cache' and store in 'conf.d' which is a subdir of 'LogDir' point to." default:""` IpcSocketPath string `long:"ipc-sockopt-path" description:"default ipc socket path /var/run/dpvs.ipc" default:"/var/run/dpvs.ipc"` restapi.Server } @@ -65,6 +71,64 @@ func unixDialer(ctx context.Context) (net.Conn, error) { return nil, errors.New("unknown error") } +func validFile(fileName string) error { + filePath := fileName[:strings.LastIndex(fileName, "/")] + pathInfo, err := os.Stat(filePath) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(filePath, os.ModePerm) + if err != nil { + return err + } + return nil + } + return err + } + + if !pathInfo.IsDir() { + return errors.New(fmt.Sprintf("%s is file", pathInfo.Name())) + } + + fileInfo, err := os.Stat(fileName) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + if fileInfo.IsDir() { + return errors.New(fmt.Sprintf("%s is dir", fileInfo.Name())) + } + + return nil +} + +func getFilePath(baseDir, defaultSubdir, targetFile, defaultName string) string { + if len(targetFile) == 0 || strings.EqualFold(targetFile, "/") { + return filepath.Join(baseDir, defaultSubdir, defaultName) + } else { + if strings.HasPrefix(targetFile, "/") { + if strings.HasSuffix(targetFile, "/") { + return filepath.Join(targetFile, defaultName) + } else { + return targetFile + } + } else { + if strings.Count(targetFile, "/") == 0 { + return filepath.Join(baseDir, defaultSubdir, targetFile) + } else { + if strings.HasSuffix(targetFile, "/") { + return filepath.Join(baseDir, targetFile, defaultName) + } else { + return filepath.Join(baseDir, targetFile) + } + } + } + } + return targetFile +} + func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { if strings.HasSuffix(agent.IpcSocketPath, ".ipc") { s, err := os.Stat(agent.IpcSocketPath) @@ -93,12 +157,17 @@ func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { } } - sep := "/" - if strings.HasSuffix(logDir, "/") { - sep = "" + cacheFile := getFilePath(logDir, "conf.d", agent.CacheFile, "dpvs.cache") + if err := validFile(cacheFile); err != nil { + panic(err) } + appConf := settings.ShareAppConfig() + appConf.CacheFile = cacheFile - logFile := strings.Join([]string{logDir, "dpvs-agent.log"}, sep) + logFile := getFilePath(logDir, ".", "", "dpvs-agent.log") + if err := validFile(logFile); err != nil { + panic(err) + } // logOpt := &hclog.LoggerOptions{Name: logFile} var logOpt *hclog.LoggerOptions logFileNamePattern := strings.Join([]string{logFile, "%Y%m%d%H%M"}, "-") @@ -109,7 +178,6 @@ func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { rotatelogs.WithLinkName(logFile), rotatelogs.WithRotationTime(logRotationInterval), ) - // f, err := os.Create(logFile) if err == nil { logOpt = &hclog.LoggerOptions{Name: logFile, Output: logF} } else { @@ -120,6 +188,8 @@ func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { logger := hclog.Default().Named("main") + //////////////////////////////////// ipvs /////////////////////////////////////////// + // delete restAPI.VirtualserverDeleteVsVipPortHandler = ipvs.NewDelVsItem(cp, logger) restAPI.VirtualserverDeleteVsVipPortLaddrHandler = ipvs.NewDelVsLaddr(cp, logger) @@ -136,12 +206,15 @@ func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { restAPI.VirtualserverPutVsVipPortHandler = ipvs.NewPutVsItem(cp, logger) restAPI.VirtualserverPutVsVipPortLaddrHandler = ipvs.NewPutVsLaddr(cp, logger) restAPI.VirtualserverPutVsVipPortRsHandler = ipvs.NewPutVsRs(cp, logger) + restAPI.VirtualserverPutVsVipPortRsHealthHandler = ipvs.NewPutVsRsHealth(cp, logger) restAPI.VirtualserverPutVsVipPortDenyHandler = ipvs.NewPutVsDeny(cp, logger) restAPI.VirtualserverPutVsVipPortAllowHandler = ipvs.NewPutVsAllow(cp, logger) // post restAPI.VirtualserverPostVsVipPortRsHandler = ipvs.NewPostVsRs(cp, logger) + //////////////////////////////////// device /////////////////////////////////////////// + // get // restAPI.DeviceGetDeviceNameAddrHandler // restAPI.DeviceGetDeviceNameRouteHandler @@ -161,6 +234,31 @@ func (agent *DpvsAgentServer) instantiateAPI(restAPI *operations.DpvsAgentAPI) { restAPI.DeviceDeleteDeviceNameRouteHandler = device.NewDelDeviceRoute(cp, logger) restAPI.DeviceDeleteDeviceNameVlanHandler = device.NewDelDeviceVlan(cp, logger) restAPI.DeviceDeleteDeviceNameNetlinkAddrHandler = device.NewDelDeviceNetlinkAddr(cp, logger) + + //////////////////////////////////// ipset /////////////////////////////////////////// + + // GET + restAPI.IpsetGetHandler = ipset.NewIpsetGet(cp, logger) + restAPI.IpsetGetAllHandler = ipset.NewIpsetGetAll(cp, logger) + + // POST + restAPI.IpsetIsInHandler = ipset.NewIpsetIsIn(cp, logger) + restAPI.IpsetAddMemberHandler = ipset.NewIpsetAddMember(cp, logger) + + // PUT + restAPI.IpsetCreateHandler = ipset.NewIpsetCreate(cp, logger) + restAPI.IpsetReplaceMemberHandler = ipset.NewIpsetReplaceMember(cp, logger) + + // DELETE + restAPI.IpsetDestroyHandler = ipset.NewIpsetDestroy(cp, logger) + restAPI.IpsetDelMemberHandler = ipset.NewIpsetDelMember(cp, logger) + + switch strings.ToLower(agent.InitMode) { + case "network": + case "local": + agent.Host = "127.0.0.1" + agent.LocalLoad(cp, logger) + } } func (agent *DpvsAgentServer) InstantiateServer(api *operations.DpvsAgentAPI) *restapi.Server { diff --git a/tools/dpvs-agent/cmd/dpvs-agent-server/local_init.go b/tools/dpvs-agent/cmd/dpvs-agent-server/local_init.go new file mode 100644 index 000000000..b8bb050e5 --- /dev/null +++ b/tools/dpvs-agent/cmd/dpvs-agent-server/local_init.go @@ -0,0 +1,122 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "strings" + + "github.com/dpvs-agent/cmd/device" + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" + + "github.com/hashicorp/go-hclog" +) + +func (agent *DpvsAgentServer) LocalLoad(cp *pool.ConnPool, parentLogger hclog.Logger) error { + var errs []error + logger := hclog.Default().Named("LoadConfigFile") + if parentLogger != nil { + logger = parentLogger.Named("LoadConfigFile") + } + + nodeSnap := settings.ShareSnapshot() + if err := nodeSnap.LoadFrom(settings.LocalConfigFile(), logger); err != nil { + return err + } + + announcePort := nodeSnap.NodeSpec.AnnouncePort + laddrs := nodeSnap.NodeSpec.Laddrs + + for _, snap := range nodeSnap.Snapshot { + service := snap.Service + // 1> ipvsadm -A vip:port -s wrr + vs := types.NewVirtualServerSpec() + vs.SetAddr(service.Addr) + vs.SetPort(service.Port) + vs.SetProto(service.Proto) + vs.SetFwmark(service.Fwmark) + vs.SetConnTimeout(service.ConnTimeout) + vs.SetBps(service.Bps) + vs.SetLimitProportion(service.LimitProportion) + vs.SetTimeout(service.Timeout) + vs.SetSchedName(service.SchedName) + flags := strings.ToLower(service.Flags) + if strings.Index(flags, "expirequiescent") != -1 { + vs.SetFlagsExpireQuiescent() + } + if strings.Index(flags, "synproxy") != -1 { + vs.SetFlagsSynProxy() + } + if strings.Index(flags, "conhashbysrcip") != -1 { + vs.SetFlagsHashSrcIP() + } + if strings.Index(flags, "conhashbyquicid") != -1 { + vs.SetFlagsHashQuicID() + } + vs.Add(cp, logger) + // 2> dpip addr add ${vip} dev ${device} + svcAddr := types.NewInetAddrDetail() + svcAddr.SetAddr(service.Addr) + svcAddr.SetIfName(announcePort.Dpvs) + svcAddr.Add(cp, logger) + + // 3> ipvsadm -at ${VIPPORT} -r ${RS:PORT} -w ${WEIGHT} -b + rsFront := types.NewRealServerFront() + if err := rsFront.ParseVipPortProto(vs.ID()); err != nil { + errs = append(errs, err) + } + rss := make([]*types.RealServerSpec, len(service.RSs.Items)) + for i, rs := range service.RSs.Items { + var fwdmode types.DpvsFwdMode + fwdmode.FromString(rs.Spec.Mode) + rss[i] = types.NewRealServerSpec() + rss[i].SetPort(rs.Spec.Port) + rss[i].SetWeight(uint32(rs.Spec.Weight)) + rss[i].SetProto(uint16(service.Proto)) + rss[i].SetAddr(rs.Spec.IP) + rss[i].SetFwdMode(fwdmode) + } + + rsFront.Update(rss, cp, logger) + // 4> bind laddr with vs (ipvsadm --add-laddr -z ${LADDR} -t ${VIPPORT} -F ${device}) + laddr := types.NewLocalAddrFront() + if err := laddr.ParseVipPortProto(vs.ID()); err != nil { + } + lds := make([]*types.LocalAddrDetail, len(laddrs.Items)) + for i, lip := range laddrs.Items { + lds[i] = types.NewLocalAddrDetail() + lds[i].SetAddr(lip.Addr) + lds[i].SetIfName(lip.Device) + } + laddr.Add(lds, cp, logger) + // 5> ip addr add ${VIP} dev ${KNIDEVICE(lo?)} + if err := device.NetlinkAddrAdd(service.Addr, announcePort.Switch, logger); err != nil { + logger.Error("add addr", service.Addr, "onto device failed") + errs = append(errs, err) + } + } + // 6> dpip addr add ${LADDR} dev ${device} + for _, lip := range laddrs.Items { + lipAddr := types.NewInetAddrDetail() + lipAddr.SetAddr(lip.Addr) + lipAddr.SetIfName(lip.Device) + lipAddr.SetFlags("sapool") + resultCode := lipAddr.Add(cp, logger) + logger.Info("Add addr to device done.", "Device", lip.Device, "Addr", lip.Addr, "result", resultCode.String()) + } + + return settings.MergedError(errs) +} diff --git a/tools/dpvs-agent/cmd/ipset/add_member.go b/tools/dpvs-agent/cmd/ipset/add_member.go new file mode 100644 index 000000000..71efaca51 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/add_member.go @@ -0,0 +1,79 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetAddMember struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetAddMember(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetAddMember { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetAddMember") + } + return &ipsetAddMember{connPool: cp, logger: logger} +} + +func (h *ipsetAddMember) Handle(params api.AddMemberParams) middleware.Responder { + if params.IpsetParam == nil { + return api.NewAddMemberBadRequest().WithPayload("missing ipset param") + } + + if *params.IpsetParam.Name != params.Name { + return api.NewAddMemberBadRequest().WithPayload("ipset name mismatch") + } + + if params.IpsetParam.CreationOptions != nil { + return api.NewAddMemberBadRequest().WithPayload("CreationOptions set in adding member") + } + + conf := types.IPSetParamArray{} + if err := conf.Build(types.IPSET_OP_ADD, params.IpsetParam); err != nil { + return api.NewAddMemberBadRequest().WithPayload(fmt.Sprintf( + "build AddMember param failed: %s", err.Error())) + } + + if err := conf.Check(); err != nil { + return api.NewAddMemberBadRequest().WithPayload(fmt.Sprintf( + "AddMember params check failed: %s", err.Error())) + } + + err, derr := conf.AddDelMember(h.connPool, h.logger) + if derr == types.EDPVS_EXIST { + return api.NewAddMemberOK().WithPayload(fmt.Sprintf("%s (may partially succeed)", derr.String())) + } + if err != nil { + h.logger.Error("Ipset AddMember failed.", "setName", params.Name, "Reason", err.Error()) + if derr == types.EDPVS_NOTEXIST { + return api.NewAddMemberNotFound().WithPayload(derr.String()) + } + return api.NewAddMemberFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset AddMember succeed.", "setName", params.Name) + return api.NewAddMemberCreated().WithPayload(fmt.Sprintf("ipset %s add members succeed", + params.Name)) +} diff --git a/tools/dpvs-agent/cmd/ipset/create.go b/tools/dpvs-agent/cmd/ipset/create.go new file mode 100644 index 000000000..5d5925872 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/create.go @@ -0,0 +1,75 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetCreate struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetCreate(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetCreate { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetCreate") + } + return &ipsetCreate{connPool: cp, logger: logger} +} + +func (h *ipsetCreate) Handle(params api.CreateParams) middleware.Responder { + if params.IpsetParam == nil { + return api.NewCreateBadRequest().WithPayload("missing ipset param") + } + + if *params.IpsetParam.Name != params.Name { + return api.NewCreateBadRequest().WithPayload("ipset name mismatch") + } + + conf := types.IPSetParam{} + conf.SetOpcode(types.IPSET_OP_CREATE) + if err := conf.Build(params.IpsetParam); err != nil { + return api.NewCreateBadRequest().WithPayload(fmt.Sprintf( + "build create param failed: %s", err.Error())) + } + + if err := conf.Check(); err != nil { + return api.NewCreateBadRequest().WithPayload(fmt.Sprintf("invalid create params: %s", + err.Error())) + } + + err, derr := conf.CreateDestroy(h.connPool, h.logger) + if derr == types.EDPVS_EXIST { + return api.NewCreateOK().WithPayload(derr.String()) + } + if err != nil { + h.logger.Error("Ipset Create failed.", "setName", params.Name, "Reason", err.Error()) + if derr == types.EDPVS_NOTEXIST { + return api.NewCreateNotFound().WithPayload(derr.String()) + } + return api.NewCreateFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset Create succeed.", "setName", params.Name) + return api.NewCreateCreated().WithPayload(fmt.Sprintf("ipset %s created", params.Name)) +} diff --git a/tools/dpvs-agent/cmd/ipset/del_member.go b/tools/dpvs-agent/cmd/ipset/del_member.go new file mode 100644 index 000000000..fa2a8532c --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/del_member.go @@ -0,0 +1,77 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetDelMember struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetDelMember(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetDelMember { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetDelMember") + } + return &ipsetDelMember{connPool: cp, logger: logger} +} + +func (h *ipsetDelMember) Handle(params api.DelMemberParams) middleware.Responder { + if params.IpsetParam == nil { + return api.NewDelMemberBadRequest().WithPayload("missing ipset param") + } + + if *params.IpsetParam.Name != params.Name { + return api.NewDelMemberBadRequest().WithPayload("ipset name mismatch") + } + + if params.IpsetParam.CreationOptions != nil { + return api.NewDelMemberBadRequest().WithPayload("CreationOptions set in deleting member") + } + + conf := types.IPSetParamArray{} + if err := conf.Build(types.IPSET_OP_DEL, params.IpsetParam); err != nil { + return api.NewDelMemberBadRequest().WithPayload(fmt.Sprintf( + "build DelMember param failed: %s", err.Error())) + } + + if err := conf.Check(); err != nil { + return api.NewDelMemberBadRequest().WithPayload(fmt.Sprintf( + "DelMember params check failed: %s", err.Error())) + } + + err, derr := conf.AddDelMember(h.connPool, h.logger) + if derr == types.EDPVS_NOTEXIST { + return api.NewDelMemberNotFound().WithPayload(fmt.Sprintf("%s(may partially deleted)", + derr.String())) + } + if err != nil { + h.logger.Error("Ipset DelMember failed.", "setName", params.Name, "Reason", err.Error()) + return api.NewDelMemberFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset DelMember succeed.", "setName", params.Name) + return api.NewDelMemberOK().WithPayload(fmt.Sprintf("ipset %s delete members succeed", + params.Name)) +} diff --git a/tools/dpvs-agent/cmd/ipset/destroy.go b/tools/dpvs-agent/cmd/ipset/destroy.go new file mode 100644 index 000000000..b44e1d53e --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/destroy.go @@ -0,0 +1,56 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetDestroy struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetDestroy(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetDestroy { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetDestroy") + } + return &ipsetDestroy{connPool: cp, logger: logger} +} + +func (h *ipsetDestroy) Handle(params api.DestroyParams) middleware.Responder { + conf := types.IPSetParam{} + conf.SetOpcode(types.IPSET_OP_DESTROY) + conf.SetName(params.Name) + + err, derr := conf.CreateDestroy(h.connPool, h.logger) + if derr == types.EDPVS_NOTEXIST { + return api.NewDestroyNotFound().WithPayload(derr.String()) + } + if err != nil { + h.logger.Error("Ipset Destroy failed.", "setName", params.Name, "Reason", err.Error()) + return api.NewDestroyFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset Destroy succeed.", "setName", params.Name) + return api.NewDestroyOK().WithPayload(fmt.Sprintf("ipset %s destroyed", params.Name)) +} diff --git a/tools/dpvs-agent/cmd/ipset/get.go b/tools/dpvs-agent/cmd/ipset/get.go new file mode 100644 index 000000000..998321e52 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/get.go @@ -0,0 +1,63 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetGet struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetGet(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetGet { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetGet") + } + return &ipsetGet{connPool: cp, logger: logger} +} + +func (h *ipsetGet) Handle(params api.GetParams) middleware.Responder { + conf := &types.IPSetParam{} + + conf.SetOpcode(types.IPSET_OP_LIST) + conf.SetName(params.Name) + infos, err, derr := conf.Get(h.connPool, h.logger) + if err != nil { + h.logger.Error("Ipset Get failed.", "setName", params.Name, "Reason", err.Error()) + if derr == types.EDPVS_NOTEXIST { + return api.NewGetNotFound().WithPayload(derr.String()) + } + return api.NewGetFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset Get succeed", "setName", params.Name) + model, err := infos.Model() + if err != nil { + h.logger.Error("Modelling ipset Get result failed.", "setName", params.Name, "Reason", err.Error()) + } + + resp := api.NewGetOK() + if model.Count > 0 { + resp.SetPayload(model.Infos[0]) + } + return resp +} diff --git a/tools/dpvs-agent/cmd/ipset/get_all.go b/tools/dpvs-agent/cmd/ipset/get_all.go new file mode 100644 index 000000000..2e654e92e --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/get_all.go @@ -0,0 +1,54 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetGetAll struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetGetAll(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetGetAll { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetGetAll") + } + return &ipsetGetAll{connPool: cp, logger: logger} +} + +func (h *ipsetGetAll) Handle(params api.GetAllParams) middleware.Responder { + conf := &types.IPSetParam{} + + conf.SetOpcode(types.IPSET_OP_LIST) + infos, err, _ := conf.Get(h.connPool, h.logger) + if err != nil { + h.logger.Error("Ipset GetAll failed.", "Reason", err.Error()) + return api.NewGetAllFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset GetAll succeed") + model, err := infos.Model() + if err != nil { + h.logger.Error("Modelling ipset GetAll result failed.", "Reason", err.Error()) + } + return api.NewGetAllOK().WithPayload(model) +} diff --git a/tools/dpvs-agent/cmd/ipset/is_in.go b/tools/dpvs-agent/cmd/ipset/is_in.go new file mode 100644 index 000000000..eadd3f1f2 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/is_in.go @@ -0,0 +1,81 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetIsIn struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetIsIn(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetIsIn { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetIsIn") + } + return &ipsetIsIn{connPool: cp, logger: logger} +} + +func (h *ipsetIsIn) Handle(params api.IsInParams) middleware.Responder { + if params.IpsetCell == nil { + return api.NewIsInBadRequest().WithPayload("missing ipset entry") + } + + conf := types.IPSetParam{} + conf.SetOpcode(types.IPSET_OP_TEST) + conf.SetName(params.Name) + conf.SetKind(string(*params.IpsetCell.Type)) + if err := conf.BuildMember(params.IpsetCell.Member); err != nil { + return api.NewIsInBadRequest().WithPayload(fmt.Sprintf("invalid member: %s", err.Error())) + } + + if err := conf.Check(); err != nil { + return api.NewIsInBadRequest().WithPayload(fmt.Sprintf("invalid param: %s", err.Error())) + } + + result, err, derr := conf.IsIn(h.connPool, h.logger) + if err != nil { + h.logger.Error("Ipset IsIn failed.", "setName", params.Name, "Reason", err.Error()) + if derr == types.EDPVS_NOTEXIST { + return api.NewIsInNotFound().WithPayload(derr.String()) + } + return api.NewIsInFailure().WithPayload(err.Error()) + } + h.logger.Info("Ipset InIn succeed.", "setName", params.Name) + + nomatch := "" + if params.IpsetCell.Member.Options != nil && + params.IpsetCell.Member.Options.NoMatch != nil && + *params.IpsetCell.Member.Options.NoMatch { + nomatch = " (nomatch)" + } + + msg := "" + if result { + msg = fmt.Sprintf("%s%s is IN set %s", nomatch, *params.IpsetCell.Member.Entry, params.Name) + } else { + msg = fmt.Sprintf("%s%s is NOT IN set %s", nomatch, *params.IpsetCell.Member.Entry, params.Name) + } + return api.NewIsInOK().WithPayload(&api.IsInOKBody{Result: &result, Message: msg}) +} diff --git a/tools/dpvs-agent/cmd/ipset/replace_member.go b/tools/dpvs-agent/cmd/ipset/replace_member.go new file mode 100644 index 000000000..73b6c5156 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipset/replace_member.go @@ -0,0 +1,81 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipset + +import ( + "fmt" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + api "github.com/dpvs-agent/restapi/operations/ipset" + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type ipsetReplaceMember struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewIpsetReplaceMember(cp *pool.ConnPool, parentLogger hclog.Logger) *ipsetReplaceMember { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("ipsetReplaceMember") + } + return &ipsetReplaceMember{connPool: cp, logger: logger} +} + +func (h *ipsetReplaceMember) Handle(params api.ReplaceMemberParams) middleware.Responder { + if params.IpsetParam == nil { + return api.NewReplaceMemberBadRequest().WithPayload("missing ipset param") + } + + if *params.IpsetParam.Name != params.Name { + return api.NewReplaceMemberBadRequest().WithPayload("ipset name mismatch") + } + + if params.IpsetParam.CreationOptions != nil { + return api.NewReplaceMemberBadRequest().WithPayload("CreationOptions set in replacing member") + } + + opcode := types.IPSET_OP_FLUSH + if len(params.IpsetParam.Entries) > 0 { + opcode = types.IPSET_OP_ADD + } + + conf := types.IPSetParamArray{} + if err := conf.Build(opcode, params.IpsetParam); err != nil { + return api.NewReplaceMemberBadRequest().WithPayload(fmt.Sprintf( + "build ReplaceMember param failed: %s", err.Error())) + } + + if err := conf.Check(); err != nil { + return api.NewReplaceMemberBadRequest().WithPayload(fmt.Sprintf( + "ReplaceMember params check failed: %s", err.Error())) + } + + err, derr := conf.ReplaceMember(h.connPool, h.logger) + if derr == types.EDPVS_NOTEXIST { + return api.NewReplaceMemberNotFound().WithPayload(derr.String()) + } + if err != nil { + h.logger.Error("Ipset ReplaceMember failed.", "setName", params.Name, "Reason", err.Error()) + return api.NewReplaceMemberFailure().WithPayload(err.Error()) + } + + h.logger.Info("Ipset ReplaceMember succeed.", "setName", params.Name) + return api.NewReplaceMemberOK().WithPayload(fmt.Sprintf("ipset %s replace members succeed", + params.Name)) +} diff --git a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port.go b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port.go index 280827cee..9e0429e08 100644 --- a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port.go +++ b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port.go @@ -17,6 +17,7 @@ package ipvs import ( "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -44,12 +45,21 @@ func (h *delVsItem) Handle(params apiVs.DeleteVsVipPortParams) middleware.Respon return apiVs.NewDeleteVsVipPortFailure() } + shareSnapshot := settings.ShareSnapshot() + snapshot := shareSnapshot.SnapshotGet(params.VipPort) + if snapshot != nil { + snapshot.Lock() + defer snapshot.Unlock() + } + result := vs.Del(h.connPool, h.logger) switch result { case types.EDPVS_OK: + shareSnapshot.ServiceDel(params.VipPort) h.logger.Info("Del virtual server success.", "VipPort", params.VipPort) return apiVs.NewDeleteVsVipPortOK() case types.EDPVS_NOTEXIST: + shareSnapshot.ServiceDel(params.VipPort) h.logger.Warn("Del a not exist virtual server done.", "VipPort", params.VipPort, "result", result.String()) return apiVs.NewDeleteVsVipPortNotFound() default: diff --git a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_allow.go b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_allow.go index f1e068e5d..ab04f776e 100644 --- a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_allow.go +++ b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_allow.go @@ -16,6 +16,7 @@ package ipvs import ( "net" + "strings" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" @@ -48,18 +49,29 @@ func (h *delVsAllow) Handle(params apiVs.DeleteVsVipPortAllowParams) middleware. failed := false for _, allow := range params.ACL.Items { - if net.ParseIP(allow.Addr) == nil { - h.logger.Error("Invalid ip addr del.", "VipPort", params.VipPort, "Addr", allow.Addr) - return apiVs.NewDeleteVsVipPortAllowInvalidFrontend() + spec.SetCaddr("") + spec.SetIpset("") + if len(allow.Ipset) > 0 { + if !strings.HasPrefix(allow.Ipset, "ipset:") { + h.logger.Error("Invalid allow ipset format in del.", "VipPort", params.VipPort, + "Ipset", allow.Ipset, "expecting \"ipset:NAME\"") + return apiVs.NewPutVsVipPortAllowInvalidFrontend() + } + spec.SetIpset(allow.Ipset) + } else { + if net.ParseIP(allow.Addr) == nil { + h.logger.Error("Invalid ip addr del in del.", "VipPort", params.VipPort, "Addr", allow.Addr) + return apiVs.NewDeleteVsVipPortAllowInvalidFrontend() + } + spec.SetCaddr(allow.Addr) } - spec.SetSrc(allow.Addr) if result := spec.Del(h.connPool, false, h.logger); result != types.EDPVS_OK { failed = true h.logger.Error("IP Addr delete from white list failed.", "VipPort", params.VipPort, "Addr", allow.Addr, "result", result.String()) continue } - h.logger.Info("IP Addr delete from white list success.", "VipPort", params.VipPort, "Addr", allow.Addr) + h.logger.Info("Delete entry from black list success.", "VipPort", params.VipPort, "Addr", allow.Addr, "Ipset", allow.Ipset) } if failed { diff --git a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_deny.go b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_deny.go index 54d2bb9d2..f328056b2 100644 --- a/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_deny.go +++ b/tools/dpvs-agent/cmd/ipvs/delete_vs_vip_port_deny.go @@ -16,6 +16,7 @@ package ipvs import ( "net" + "strings" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" @@ -48,18 +49,29 @@ func (h *delVsDeny) Handle(params apiVs.DeleteVsVipPortDenyParams) middleware.Re failed := false for _, deny := range params.ACL.Items { - if net.ParseIP(deny.Addr) == nil { - h.logger.Error("Invalid ip addr del.", "VipPort", params.VipPort, "Addr", deny.Addr) - return apiVs.NewDeleteVsVipPortDenyInvalidFrontend() + spec.SetCaddr("") + spec.SetIpset("") + if len(deny.Ipset) > 0 { + if !strings.HasPrefix(deny.Ipset, "ipset:") { + h.logger.Error("Invalid deny ipset format in del.", "VipPort", params.VipPort, + "Ipset", deny.Ipset, "expecting \"ipset:NAME\"") + return apiVs.NewPutVsVipPortDenyInvalidFrontend() + } + spec.SetIpset(deny.Ipset) + } else { + if net.ParseIP(deny.Addr) == nil { + h.logger.Error("Invalid ip addr in del.", "VipPort", params.VipPort, "Addr", deny.Addr) + return apiVs.NewDeleteVsVipPortDenyInvalidFrontend() + } + spec.SetCaddr(deny.Addr) } - spec.SetSrc(deny.Addr) if result := spec.Del(h.connPool, true, h.logger); result != types.EDPVS_OK { h.logger.Error("IP Addr delete from black list failed.", "VipPort", params.VipPort, "Addr", deny.Addr, "result", result.String()) failed = true continue } - h.logger.Info("IP Addr delete from black list success.", "VipPort", params.VipPort, "Addr", deny.Addr) + h.logger.Info("Delete entry from black list success.", "VipPort", params.VipPort, "Addr", deny.Addr, "Ipset", deny.Ipset) } if failed { diff --git a/tools/dpvs-agent/cmd/ipvs/get_vs.go b/tools/dpvs-agent/cmd/ipvs/get_vs.go index 700931f75..e3247cacd 100644 --- a/tools/dpvs-agent/cmd/ipvs/get_vs.go +++ b/tools/dpvs-agent/cmd/ipvs/get_vs.go @@ -18,6 +18,7 @@ import ( "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -39,17 +40,27 @@ func NewGetVs(cp *pool.ConnPool, parentLogger hclog.Logger) *getVs { } func (h *getVs) Handle(params apiVs.GetVsParams) middleware.Responder { + shareSnapshot := settings.ShareSnapshot() + if params.Healthcheck != nil && !*params.Healthcheck { + return apiVs.NewGetVsOK().WithPayload(shareSnapshot.GetModels(h.logger)) + } + + // if params.Snapshot != nil && *params.Snapshot { + // shareSnapshot.DumpTo(settings.LocalConfigFile(), h.logger) + // } + front := types.NewVirtualServerFront() vss, err := front.Get(h.connPool, h.logger) if err != nil { h.logger.Error("Get virtual server list failed.", "Error", err.Error()) - // FIXME: Invalid - return apiVs.NewGetVsOK() + return apiVs.NewGetVsNoContent() + } + + vsModels := models.VirtualServerList{ + Items: make([]*models.VirtualServerSpecExpand, len(vss)), } h.logger.Info("Get all virtual server done.", "vss", vss) - vsModels := new(models.VirtualServerList) - vsModels.Items = make([]*models.VirtualServerSpecExpand, len(vss)) for i, vs := range vss { front := types.NewRealServerFront() @@ -81,5 +92,5 @@ func (h *getVs) Handle(params apiVs.GetVsParams) middleware.Responder { } } - return apiVs.NewGetVsOK().WithPayload(vsModels) + return apiVs.NewGetVsOK().WithPayload(&vsModels) } diff --git a/tools/dpvs-agent/cmd/ipvs/get_vs_vip_port.go b/tools/dpvs-agent/cmd/ipvs/get_vs_vip_port.go index c9f907876..da9373711 100644 --- a/tools/dpvs-agent/cmd/ipvs/get_vs_vip_port.go +++ b/tools/dpvs-agent/cmd/ipvs/get_vs_vip_port.go @@ -15,9 +15,12 @@ package ipvs import ( + "strings" + "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -39,10 +42,33 @@ func NewGetVsVipPort(cp *pool.ConnPool, parentLogger hclog.Logger) *getVsVipPort } func (h *getVsVipPort) Handle(params apiVs.GetVsVipPortParams) middleware.Responder { + shareSnapshot := settings.ShareSnapshot() + if params.Healthcheck != nil && !*params.Healthcheck { + vsModel := shareSnapshot.ServiceGet(params.VipPort) + if vsModel != nil { + vsModels := new(models.VirtualServerList) + vsModels.Items = make([]*models.VirtualServerSpecExpand, 1) + vsModels.Items[0] = vsModel + return apiVs.NewGetVsVipPortOK().WithPayload(vsModels) + } + } + + vaild := true var vss []*types.VirtualServerSpec spec := types.NewVirtualServerSpec() err := spec.ParseVipPortProto(params.VipPort) if err != nil { + vaild = false + if params.Healthcheck != nil && !*params.Healthcheck { + // invalid VipPort string + // respond full cache info + vsModels := shareSnapshot.GetModels(h.logger) + if len(vsModels.Items) != 0 { + return apiVs.NewGetVsVipPortOK().WithPayload(vsModels) + } + // read from dpvs memory + } + h.logger.Warn("Convert to virtual server failed. Get All virtual server.", "VipPort", params.VipPort, "Error", err.Error()) front := types.NewVirtualServerFront() vss, err = front.Get(h.connPool, h.logger) @@ -55,8 +81,9 @@ func (h *getVsVipPort) Handle(params apiVs.GetVsVipPortParams) middleware.Respon return apiVs.NewGetVsVipPortNotFound() } - vsModels := new(models.VirtualServerList) - vsModels.Items = make([]*models.VirtualServerSpecExpand, len(vss)) + vsModels := &models.VirtualServerList{ + Items: make([]*models.VirtualServerSpecExpand, len(vss)), + } for i, vs := range vss { front := types.NewRealServerFront() @@ -76,18 +103,33 @@ func (h *getVsVipPort) Handle(params apiVs.GetVsVipPortParams) middleware.Respon h.logger.Info("Get real server list of virtual server success.", "ID", vs.ID(), "rss", rss) - vsModels.Items[i] = vs.GetModel() - vsStats := (*types.ServerStats)(vsModels.Items[i].Stats) - vsModels.Items[i].RSs = new(models.RealServerExpandList) - vsModels.Items[i].RSs.Items = make([]*models.RealServerSpecExpand, len(rss)) + vsModel := vs.GetModel() + vsModels.Items[i] = vsModel + vsStats := (*types.ServerStats)(vsModel.Stats) + vsModel.RSs = new(models.RealServerExpandList) + vsModel.RSs.Items = make([]*models.RealServerSpecExpand, len(rss)) for j, rs := range rss { rsModel := rs.GetModel() rsStats := (*types.ServerStats)(rsModel.Stats) - vsModels.Items[i].RSs.Items[j] = rsModel + vsModel.RSs.Items[j] = rsModel vsStats.Increase(rsStats) } } + if vaild { + targetModels := &models.VirtualServerList{ + Items: make([]*models.VirtualServerSpecExpand, 1), + } + + for _, vsModel := range vsModels.Items { + typesVsModel := (*types.VirtualServerSpecExpandModel)(vsModel) + if strings.EqualFold(spec.ID(), typesVsModel.ID()) { + targetModels.Items[0] = vsModel + return apiVs.NewGetVsVipPortOK().WithPayload(targetModels) + } + } + } + return apiVs.NewGetVsVipPortOK().WithPayload(vsModels) } diff --git a/tools/dpvs-agent/cmd/ipvs/post_vs_vip_port_rs.go b/tools/dpvs-agent/cmd/ipvs/post_vs_vip_port_rs.go index a7c9378a1..80bd06a39 100644 --- a/tools/dpvs-agent/cmd/ipvs/post_vs_vip_port_rs.go +++ b/tools/dpvs-agent/cmd/ipvs/post_vs_vip_port_rs.go @@ -15,9 +15,12 @@ package ipvs import ( - // "github.com/dpvs-agent/models" + "strings" + + "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -45,6 +48,10 @@ func (h *postVsRs) Handle(params apiVs.PostVsVipPortRsParams) middleware.Respond return apiVs.NewPostVsVipPortRsInvalidFrontend() } + if params.Rss == nil || params.Rss.Items == nil { + return apiVs.NewPostVsVipPortRsInvalidFrontend() + } + rss := make([]*types.RealServerSpec, len(params.Rss.Items)) for i, rs := range params.Rss.Items { var fwdmode types.DpvsFwdMode @@ -55,14 +62,58 @@ func (h *postVsRs) Handle(params apiVs.PostVsVipPortRsParams) middleware.Respond rss[i].SetWeight(uint32(rs.Weight)) rss[i].SetProto(front.GetProto()) rss[i].SetAddr(rs.IP) - rss[i].SetInhibited(rs.Inhibited) rss[i].SetOverloaded(rs.Overloaded) rss[i].SetFwdMode(fwdmode) + // NOTE: inhibited set by healthcheck module with API /vs/${ID}/rs/health only + // we clear it default + inhibited := false + if rs.Inhibited != nil { + inhibited = *rs.Inhibited + } + rss[i].SetInhibited(&inhibited) + } + + shareSnapshot := settings.ShareSnapshot() + if shareSnapshot.ServiceLock(params.VipPort) { + defer shareSnapshot.ServiceUnlock(params.VipPort) } result := front.Update(rss, h.connPool, h.logger) switch result { case types.EDPVS_EXIST, types.EDPVS_OK: + // Update Snapshot + vsModel := shareSnapshot.ServiceGet(params.VipPort) + if vsModel == nil { + spec := types.NewVirtualServerSpec() + err := spec.ParseVipPortProto(params.VipPort) + if err != nil { + h.logger.Warn("Convert to virtual server failed.", "VipPort", params.VipPort, "Error", err.Error()) + // FIXME return + } + vss, err := spec.Get(h.connPool, h.logger) + if err != nil { + h.logger.Error("Get virtual server failed.", "svc VipPort", params.VipPort, "Error", err.Error()) + // FIXME return + } + + for _, vs := range vss { + if strings.EqualFold(vs.ID(), spec.ID()) { + shareSnapshot.ServiceAdd(vs) + break + } + } + } else { + vsModel.RSs = &models.RealServerExpandList{ + Items: make([]*models.RealServerSpecExpand, len(rss)), + } + + for i, rs := range rss { + vsModel.RSs.Items[i] = rs.GetModel() + } + } + + shareSnapshot.ServiceVersionUpdate(params.VipPort, h.logger) + h.logger.Info("Set real server to virtual server success.", "VipPort", params.VipPort, "rss", rss, "result", result.String()) return apiVs.NewPostVsVipPortRsOK() default: diff --git a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port.go b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port.go index e96b4c697..52328290d 100644 --- a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port.go +++ b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port.go @@ -17,9 +17,10 @@ package ipvs import ( "strings" - // "github.com/dpvs-agent/models" + "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" "golang.org/x/sys/unix" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -69,6 +70,10 @@ func (h *putVsItem) Handle(params apiVs.PutVsVipPortParams) middleware.Responder vs.SetFlagsExpireQuiescent() } + if params.Spec.Quic != nil && *params.Spec.Quic { + vs.SetFlagsQuic() + } + if params.Spec.SynProxy != nil && *params.Spec.SynProxy { vs.SetFlagsSynProxy() } @@ -85,22 +90,60 @@ func (h *putVsItem) Handle(params apiVs.PutVsVipPortParams) middleware.Responder } } + shareSnapshot := settings.ShareSnapshot() result := vs.Add(h.connPool, h.logger) h.logger.Info("Add virtual server done.", "vs", vs, "result", result.String()) switch result { case types.EDPVS_OK: // return 201 + shareSnapshot.ServiceAdd(vs) h.logger.Info("Created new virtual server success.", "VipPort", params.VipPort) return apiVs.NewPutVsVipPortCreated() case types.EDPVS_EXIST: h.logger.Info("The virtual server already exist! Try to update.", "VipPort", params.VipPort) + + if shareSnapshot.ServiceLock(vs.ID()) { + defer shareSnapshot.ServiceUnlock(vs.ID()) + } + reason := vs.Update(h.connPool, h.logger) if reason != types.EDPVS_OK { // return 461 h.logger.Error("Update virtual server failed.", "VipPort", params.VipPort, "reason", reason.String()) return apiVs.NewPutVsVipPortInvalidBackend() } + + newVsModel := vs.GetModel() + vsModel := shareSnapshot.ServiceGet(vs.ID()) + if vsModel == nil { + newVsModel.RSs = &models.RealServerExpandList{ + Items: make([]*models.RealServerSpecExpand, 0), + } + shareSnapshot.ServiceUpsert(newVsModel) + return apiVs.NewPutVsVipPortOK() + } + + vsModel.Bps = newVsModel.Bps + vsModel.ConnTimeout = newVsModel.ConnTimeout + vsModel.LimitProportion = newVsModel.LimitProportion + vsModel.ExpireQuiescent = newVsModel.ExpireQuiescent + vsModel.Quic = newVsModel.Quic + vsModel.Fwmark = newVsModel.Fwmark + vsModel.SynProxy = newVsModel.SynProxy + vsModel.Match = newVsModel.Match + vsModel.SchedName = newVsModel.SchedName + vsModel.Timeout = newVsModel.Timeout + vsModel.Flags = newVsModel.Flags + if vsModel.RSs == nil { + vsModel.RSs = &models.RealServerExpandList{} + } + + if vsModel.RSs.Items == nil { + vsModel.RSs.Items = make([]*models.RealServerSpecExpand, 0) + } + h.logger.Info("Update virtual server success.", "VipPort", params.VipPort) + // return 200 return apiVs.NewPutVsVipPortOK() default: diff --git a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_allow.go b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_allow.go index a5ff5753a..5227b314b 100644 --- a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_allow.go +++ b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_allow.go @@ -17,6 +17,7 @@ package ipvs import ( // "fmt" "net" + "strings" // "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" @@ -50,18 +51,29 @@ func (h *putVsAllow) Handle(params apiVs.PutVsVipPortAllowParams) middleware.Res failed := false for _, allow := range params.ACL.Items { - if net.ParseIP(allow.Addr) == nil { - h.logger.Error("Invalid ip addr add.", "VipPort", params.VipPort, "Addr", allow.Addr) - return apiVs.NewPutVsVipPortAllowInvalidFrontend() + spec.SetCaddr("") + spec.SetIpset("") + if len(allow.Ipset) > 0 { + if !strings.HasPrefix(allow.Ipset, "ipset:") { + h.logger.Error("Invalid allow ipset format in add.", "VipPort", params.VipPort, + "Ipset", allow.Ipset, "expecting \"ipset:NAME\"") + return apiVs.NewPutVsVipPortAllowInvalidFrontend() + } + spec.SetIpset(allow.Ipset) + } else { + if net.ParseIP(allow.Addr) == nil { + h.logger.Error("Invalid ip addr add.", "VipPort", params.VipPort, "Addr", allow.Addr) + return apiVs.NewPutVsVipPortAllowInvalidFrontend() + } + spec.SetCaddr(allow.Addr) } - spec.SetSrc(allow.Addr) if result := spec.Add(h.connPool, false, h.logger); result != types.EDPVS_OK { failed = true h.logger.Error("Add ip addr to white list failed.", "VipPort", params.VipPort, "Addr", allow.Addr, "result", result.String()) continue } - h.logger.Info("Add ip addr to white list success.", "VipPort", params.VipPort, "Addr", allow.Addr) + h.logger.Info("Add entry to white list success.", "VipPort", params.VipPort, "Addr", allow.Addr, "Ipset", allow.Ipset) } if failed { diff --git a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_deny.go b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_deny.go index 632b563f6..5ff636871 100644 --- a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_deny.go +++ b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_deny.go @@ -17,6 +17,7 @@ package ipvs import ( // "fmt" "net" + "strings" // "github.com/dpvs-agent/models" "github.com/dpvs-agent/pkg/ipc/pool" @@ -50,18 +51,29 @@ func (h *putVsDeny) Handle(params apiVs.PutVsVipPortDenyParams) middleware.Respo failed := false for _, deny := range params.ACL.Items { - if net.ParseIP(deny.Addr) == nil { - h.logger.Error("Invalid ip addr add.", "VipPort", params.VipPort, "Addr", deny.Addr) - return apiVs.NewPutVsVipPortDenyInvalidFrontend() + spec.SetCaddr("") + spec.SetIpset("") + if len(deny.Ipset) > 0 { + if !strings.HasPrefix(deny.Ipset, "ipset:") { + h.logger.Error("Invalid deny ipset format in add.", "VipPort", params.VipPort, + "Ipset", deny.Ipset, "expecting \"ipset:NAME\"") + return apiVs.NewPutVsVipPortDenyInvalidFrontend() + } + spec.SetIpset(deny.Ipset) + } else { + if net.ParseIP(deny.Addr) == nil { + h.logger.Error("Invalid deny ip addr in add.", "VipPort", params.VipPort, "Addr", deny.Addr) + return apiVs.NewPutVsVipPortDenyInvalidFrontend() + } + spec.SetCaddr(deny.Addr) } - spec.SetSrc(deny.Addr) if result := spec.Add(h.connPool, true, h.logger); result != types.EDPVS_OK { h.logger.Error("Add ip addr to black list failed.", "VipPort", params.VipPort, "Addr", deny.Addr, "result", result.String()) failed = true continue } - h.logger.Info("Add ip addr to black list success.", "VipPort", params.VipPort, "Addr", deny.Addr) + h.logger.Info("Add entry to black list success.", "VipPort", params.VipPort, "Addr", deny.Addr, "Ipset", deny.Ipset) } if failed { diff --git a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs.go b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs.go index acfeb572f..0699daa5b 100644 --- a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs.go +++ b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs.go @@ -15,8 +15,12 @@ package ipvs import ( + "fmt" + "strings" + "github.com/dpvs-agent/pkg/ipc/pool" "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" @@ -45,7 +49,7 @@ func (h *putVsRs) Handle(params apiVs.PutVsVipPortRsParams) middleware.Responder } var rss []*types.RealServerSpec - if params.Rss != nil { + if params.Rss != nil && params.Rss.Items != nil { rss = make([]*types.RealServerSpec, len(params.Rss.Items)) for i, rs := range params.Rss.Items { var fwdmode types.DpvsFwdMode @@ -57,28 +61,59 @@ func (h *putVsRs) Handle(params apiVs.PutVsVipPortRsParams) middleware.Responder rss[i].SetProto(front.GetProto()) rss[i].SetWeight(uint32(rs.Weight)) rss[i].SetFwdMode(fwdmode) - rss[i].SetInhibited(rs.Inhibited) rss[i].SetOverloaded(rs.Overloaded) + // NOTE: inhibited set by healthcheck module with API /vs/${ID}/rs/health only + // we clear it default + inhibited := false + if rs.Inhibited != nil { + inhibited = *rs.Inhibited + } + rss[i].SetInhibited(&inhibited) } + h.logger.Info("Apply real server update.", "VipPort", params.VipPort, "rss", rss) } - healthCheck := false - if params.Healthcheck != nil { - healthCheck = *params.Healthcheck + shareSnapshot := settings.ShareSnapshot() + if shareSnapshot.ServiceLock(params.VipPort) { + defer shareSnapshot.ServiceUnlock(params.VipPort) } - result := front.Edit(healthCheck, rss, h.connPool, h.logger) + existOnly := false + result := front.Edit(existOnly, rss, h.connPool, h.logger) // h.logger.Info("Set real server sets done.", "VipPort", params.VipPort, "rss", rss, "result", result.String()) switch result { case types.EDPVS_EXIST, types.EDPVS_OK: h.logger.Info("Set real server sets success.", "VipPort", params.VipPort, "rss", rss, "result", result.String()) + // Update Snapshot + vsModel := shareSnapshot.ServiceGet(params.VipPort) + newRSs := make([]*types.RealServerSpec, 0) + for _, newRs := range rss { + exist := false + for _, cacheRs := range vsModel.RSs.Items { + rsID := fmt.Sprintf("%s:%d", cacheRs.Spec.IP, cacheRs.Spec.Port) + if strings.EqualFold(newRs.ID(), rsID) { + // update weight only + inhibited := newRs.GetInhibited() + cacheRs.Spec.Weight = uint16(newRs.GetWeight()) + cacheRs.Spec.Inhibited = &inhibited + exist = true + break + } + } + + if !exist { + newRSs = append(newRSs, newRs) + } + } + + for _, rs := range newRSs { + vsModel.RSs.Items = append(vsModel.RSs.Items, rs.GetModel()) + } + + shareSnapshot.ServiceVersionUpdate(params.VipPort, h.logger) return apiVs.NewPutVsVipPortRsOK() case types.EDPVS_NOTEXIST: - if healthCheck { - h.logger.Error("Edit not exist real server.", "VipPort", params.VipPort, "rss", rss, "result", result.String()) - return apiVs.NewPutVsVipPortRsInvalidFrontend() - } h.logger.Error("Unreachable branch") default: h.logger.Error("Set real server sets failed.", "VipPort", params.VipPort, "rss", rss, "result", result.String()) diff --git a/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs_health.go b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs_health.go new file mode 100644 index 000000000..e86b55930 --- /dev/null +++ b/tools/dpvs-agent/cmd/ipvs/put_vs_vip_port_rs_health.go @@ -0,0 +1,121 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipvs + +import ( + "fmt" + "strings" + + "github.com/dpvs-agent/models" + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/dpvs-agent/pkg/ipc/types" + "github.com/dpvs-agent/pkg/settings" + + apiVs "github.com/dpvs-agent/restapi/operations/virtualserver" + + "github.com/go-openapi/runtime/middleware" + "github.com/hashicorp/go-hclog" +) + +type putVsRsHealth struct { + connPool *pool.ConnPool + logger hclog.Logger +} + +func NewPutVsRsHealth(cp *pool.ConnPool, parentLogger hclog.Logger) *putVsRsHealth { + logger := hclog.Default() + if parentLogger != nil { + logger = parentLogger.Named("PutVsVipPortRsHealth") + } + return &putVsRsHealth{connPool: cp, logger: logger} +} +func (h *putVsRsHealth) Handle(params apiVs.PutVsVipPortRsHealthParams) middleware.Responder { + front := types.NewRealServerFront() + if err := front.ParseVipPortProto(params.VipPort); err != nil { + h.logger.Error("Convert to virtual server failed", "VipPort", params.VipPort, "Error", err.Error()) + return apiVs.NewPutVsVipPortRsHealthInvalidFrontend() + } + + activeRSs := make(map[string]*models.RealServerSpecExpand) + var vsModel *models.VirtualServerSpecExpand + shareSnapshot := settings.ShareSnapshot() + if shareSnapshot.ServiceRLock(params.VipPort) { + defer shareSnapshot.ServiceRUnlock(params.VipPort) + + vsModel = shareSnapshot.ServiceGet(params.VipPort) + if vsModel != nil { + for _, rs := range vsModel.RSs.Items { + rsModel := (*types.RealServerSpecExpandModel)(rs) + activeRSs[rsModel.ID()] = rs + } + + validRSs := make([]*types.RealServerSpec, 0) + if params.Rss != nil && params.Rss.Items != nil { + for _, rs := range params.Rss.Items { + var fwdmode types.DpvsFwdMode + fwdmode.FromString(rs.Mode) + newRs := types.NewRealServerSpec() + newRs.SetAf(front.GetAf()) + newRs.SetAddr(rs.IP) + newRs.SetPort(rs.Port) + newRs.SetProto(front.GetProto()) + newRs.SetWeight(uint32(rs.Weight)) + newRs.SetFwdMode(fwdmode) + newRs.SetInhibited(rs.Inhibited) + newRs.SetOverloaded(rs.Overloaded) + if _, existed := activeRSs[newRs.ID()]; existed { + validRSs = append(validRSs, newRs) + from := activeRSs[newRs.ID()].Spec + to := newRs + h.logger.Info("real server update.", "ID", newRs.ID(), "client Version", params.Version, "from", from, "to", to) + } + } + } + + if !strings.EqualFold(vsModel.Version, params.Version) { + h.logger.Info("The service", "VipPort", params.VipPort, "version expired. Latest Version", vsModel.Version, "Client Version", params.Version) + return apiVs.NewPutVsVipPortRsHealthUnexpected().WithPayload(vsModel) + } + + existOnly := true + result := front.Edit(existOnly, validRSs, h.connPool, h.logger) + switch result { + case types.EDPVS_EXIST, types.EDPVS_OK: + for _, newRs := range validRSs { + for _, rs := range vsModel.RSs.Items { + rsID := fmt.Sprintf("%s:%d", rs.Spec.IP, rs.Spec.Port) + if strings.EqualFold(newRs.ID(), rsID) { + inhibited := newRs.GetInhibited() + rs.Spec.Inhibited = &inhibited + break + } + } + } + h.logger.Info("Set real server sets success.", "VipPort", params.VipPort, "validRSs", validRSs, "result", result.String()) + return apiVs.NewPutVsVipPortRsHealthOK() + case types.EDPVS_NOTEXIST: + if existOnly { + h.logger.Error("Edit not exist real server.", "VipPort", params.VipPort, "validRSs", validRSs, "result", result.String()) + return apiVs.NewPutVsVipPortRsHealthInvalidFrontend() + } + h.logger.Error("Unreachable branch") + default: + h.logger.Error("Set real server sets failed.", "VipPort", params.VipPort, "validRSs", validRSs, "result", result.String()) + return apiVs.NewPutVsVipPortRsHealthInvalidBackend() + } + } + } + return apiVs.NewPutVsVipPortRsHealthFailure() +} diff --git a/tools/dpvs-agent/dpvs-agent-api.yaml b/tools/dpvs-agent/dpvs-agent-api.yaml index bd4ff3340..3e7653519 100644 --- a/tools/dpvs-agent/dpvs-agent-api.yaml +++ b/tools/dpvs-agent/dpvs-agent-api.yaml @@ -11,33 +11,35 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +--- swagger: "2.0" -info: +info: description: "dpvs agent api" version: "1.0.0" title: "dpvs agent" host: "petstore.swagger.io" basePath: "/v2" tags: -- name: "virtualserver" - description: "virtualserver" -- name: "route" - description: "route" -- name: "laddr" - description: "laddr" -- name: "tunnel" - description: "tunnel" -- name: "inetaddr" - description: "inet addr" -- name: "white_list" - description: "white list" -- name: "black_list" - description: "black list" -- name: "arp" - description: "arp" + - name: "virtualserver" + description: "virtualserver" + - name: "route" + description: "route" + - name: "laddr" + description: "laddr" + - name: "tunnel" + description: "tunnel" + - name: "inetaddr" + description: "inet addr" + - name: "white_list" + description: "white list" + - name: "black_list" + description: "black list" + - name: "arp" + description: "arp" + - name: "ipset" + description: "ipset" schemes: -- "http" + - "http" parameters: service-id: name: VipPort @@ -100,6 +102,12 @@ parameters: type: boolean default: false required: false + snapshot: + name: snapshot + in: query + type: boolean + default: true + required: false stats: name: stats in: query @@ -123,9 +131,9 @@ parameters: in: query type: string enum: - - unset - - on - - off + - unset + - on + - off default: unset required: false link: @@ -133,9 +141,9 @@ parameters: in: query type: string enum: - - unset - - up - - down + - unset + - up + - down default: unset required: false forward2kni: @@ -143,16 +151,59 @@ parameters: in: query type: string enum: - - unset - - on - - off + - unset + - on + - off default: unset required: false + version: + name: version + in: query + type: string + required: true + ipset-name: + name: name + in: path + type: string + required: true + ipset-param: + name: ipsetParam + in: body + schema: + $ref: "#/definitions/IpsetInfo" + ipset-cell: + name: ipsetCell + in: body + schema: + $ref: "#/definitions/IpsetCell" definitions: + NodeServiceSnapshot: + type: object + properties: + NodeSpec: + "$ref": "#/definitions/DpvsNodeSpec" + Services: + "$ref": "#/definitions/VirtualServerList" + DpvsNodeSpec: + type: object + properties: + AnnouncePort: + "$ref": "#/definitions/VsAnnouncePort" + Laddrs: + "$ref": "#/definitions/LocalAddressExpandList" + VsAnnouncePort: + type: object + properties: + switch: + type: string + dpvs: + type: string CertAuthSpec: properties: addr: type: string + ipset: + type: string InetAddrSpec: properties: addr: @@ -249,7 +300,7 @@ definitions: properties: Spec: "$ref": "#/definitions/RealServerSpecTiny" - Stats: + Stats: "$ref": "#/definitions/ServerStats" RealServerSpecTiny: type: object @@ -292,9 +343,6 @@ definitions: "$ref": "#/definitions/NicDeviceDetail" stats: "$ref": "#/definitions/NicDeviceStats" - #extra: - # "$ref": "#/definitions/NicDeviceStats" - #NicDeviceExtra: padding NicDeviceDetail: type: object properties: @@ -407,10 +455,10 @@ definitions: type: string description: State the component is in enum: - - Ok - - Warning - - Failure - - Disabled + - Ok + - Warning + - Failure + - Disabled msg: type: string description: Human readable status/error/warning message @@ -450,7 +498,7 @@ definitions: AddrRange: type: "object" properties: - Start: + Start: type: "string" End: type: "string" @@ -472,7 +520,8 @@ definitions: - tcp - udp - ping - + - udpping + - http VirtualServerList: type: object properties: @@ -522,6 +571,11 @@ definitions: enum: - "true" - "false" + Quic: + type: "string" + enum: + - "true" + - "false" Timeout: type: "integer" format: "uint32" @@ -539,6 +593,8 @@ definitions: format: "uint32" Addr: type: "string" + Version: + type: "string" SchedName: type: "string" enum: @@ -559,41 +615,30 @@ definitions: VirtualServerSpecTiny: type: "object" properties: - #Af: - # type: "integer" - # format: "uint16" - #Port: - # type: "integer" - # format: "uint16" Fwmark: type: "integer" format: "uint32" - #Flags: - # type: "integer" - # format: "uint32" SynProxy: type: "boolean" default: false ExpireQuiescent: type: "boolean" default: false + Quic: + type: "boolean" + default: false Timeout: type: "integer" format: "uint32" ConnTimeout: type: "integer" format: "uint32" - #Netmask: - # type: "integer" - # format: "uint32" Bps: type: "integer" format: "uint32" LimitProportion: type: "integer" format: "uint32" - #Addr: - # type: "string" ProxyProtocol: type: "string" enum: @@ -611,11 +656,148 @@ definitions: - conhash Match: "$ref": "#/definitions/MatchSpec" + IpsetOption: + description: IpsetOption defines common options for ipset operations. + type: object + properties: + NoMatch: + description: > + Nomatch excludes a small element range from an ipset, + which is mainly used by network-cidr based ipset. + type: boolean + default: false + Force: + description: > + When add members to ipset with Force set, the already existing members + are replaced sliently instead of emitting an EDPVS_EXIST error; When delete + non-existent memebers from ipset, the DPSVS_NOTEXIST error is ignored. + type: boolean + default: false + IpsetCreationOption: + description: > + IpsetCreationOption contains all available options required + in creating an ipset. + properties: + Family: + type: string + enum: + - ipv4 + - ipv6 + Comment: + type: boolean + default: false + HashSize: + type: integer + format: uint32 + HashMaxElem: + type: integer + format: uint32 + Range: + description: | + vaild format: ipv4-ipv4, ipv4/pfx, ipv6/pfx, port-port + type: string + IpsetType: + type: string + enum: + - bitmap:ip + - bitmap:ip,mac + - bitmap:port + - hash:ip + - hash:net + - hash:ip,port + - hash:net,port + - hash:net,port,iface + - hash:ip,port,ip + - hash:ip,port,net + - hash:net,port,net + - hash:net,port,net,port + IpsetMember: + description: IpsetMember represents a specific entry in ipset. + type: object + required: + - Entry + properties: + Entry: + description: | + type specific entry data, for example + * 192.168.1.0/29 (bitmap:ip) + * 192.168.88.0/24,tcp:8080-8082 (hash:net) + * 2001::1,8080-8082,2002::aaaa:bbbb:ccc0:0/108 (hash:ip,port,net) + type: string + Comment: + type: string + minLength: 1 + maxLength: 32 # IPSET_MAXCOMLEN = 32 + Options: + $ref: "#/definitions/IpsetOption" + IpsetCell: + description: IpsetCell represents an indivisible granularity of ipset member. + type: object + required: + - Type + - Member + properties: + Type: + $ref: "#/definitions/IpsetType" + Member: + $ref: "#/definitions/IpsetMember" + IpsetInfo: + description: > + IpsetInfo contains all parameters and information for ipset operations. + type: object + required: + - Type + - Name + properties: + Type: + type: string + $ref: "#/definitions/IpsetType" + Name: + type: string + minLength: 1 + maxLength: 32 # IPSET_MAXNAMELEN = 32 + Opcode: + description: opertaion type code + type: integer + format: uint16 + enum: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + x-enum-varnames: + - Add + - Del + - Test + - Create + - Destroy + - Flush + - List + CreationOptions: + $ref: '#/definitions/IpsetCreationOption' + Entries: + type: array + items: + $ref: "#/definitions/IpsetMember" + IpsetInfoArray: + description: IpsetInfoArray contains an array of ipset. + type: object + properties: + Count: + type: integer + format: int32 + Infos: + type: array + items: + $ref: "#/definitions/IpsetInfo" paths: /device: get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/stats" summary: "display all net device list" @@ -625,10 +807,9 @@ paths: schema: type: string /device/{name}/addr: - #description: dpip addr add 192.168.88.16/32 dev dpdk0.102 get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/stats" - "$ref": "#/parameters/verbose" @@ -645,8 +826,9 @@ paths: type: string put: tags: - - "device" + - "device" parameters: + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/sapool" - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/device-addr" @@ -666,7 +848,7 @@ paths: type: string delete: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/sapool" - "$ref": "#/parameters/device-name" @@ -682,10 +864,9 @@ paths: schema: type: string /device/{name}/route: - #description: dpip route add 192.168.88.16/32 dev dpdk0.102 scope kni_host get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/stats" - "$ref": "#/parameters/device-name" @@ -701,7 +882,7 @@ paths: type: string put: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/route-config" @@ -721,7 +902,7 @@ paths: type: string delete: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/route-config" @@ -738,7 +919,7 @@ paths: /device/{name}/netlink: get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/stats" @@ -754,7 +935,7 @@ paths: type: string put: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" summary: "ip link set ${name} up" @@ -769,7 +950,7 @@ paths: type: string delete: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" summary: "ip link set ${name} down" @@ -785,7 +966,7 @@ paths: /device/{name}/netlink/addr: get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/stats" @@ -801,11 +982,13 @@ paths: type: string put: tags: - - "device" + - "device" parameters: + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/device-addr" - summary: "ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device" + summary: | + ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device responses: '200': description: Success @@ -817,11 +1000,12 @@ paths: type: string delete: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/device-addr" - summary: "ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device" + summary: | + ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device responses: '200': description: Success @@ -831,12 +1015,10 @@ paths: description: Not Found schema: type: string - #/device/{name}/cpu /device/{name}/nic: - #description: dpip link show get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/verbose" @@ -846,21 +1028,22 @@ paths: '200': description: Success schema: - #type: string - "$ref": "#/definitions/NicDeviceSpecList" + "$ref": "#/definitions/NicDeviceSpecList" '500': description: Failure schema: type: string put: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/forward2kni" - "$ref": "#/parameters/link" - "$ref": "#/parameters/promisc" - summary: "dpip link set ${nic-name} [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down]" + summary: > + dpip link set ${nic-name} + [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down] responses: '200': description: Success @@ -871,10 +1054,9 @@ paths: schema: type: string /device/{name}/vlan: - #description: dpip vlan add dpdk0.102 link dpdk0 id 102 get: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/stats" @@ -890,7 +1072,7 @@ paths: type: string put: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" - "$ref": "#/parameters/vlan-config" @@ -906,7 +1088,7 @@ paths: type: string delete: tags: - - "device" + - "device" parameters: - "$ref": "#/parameters/device-name" summary: "delete special net device" @@ -922,22 +1104,29 @@ paths: /vs: get: tags: - - "virtualserver" + - "virtualserver" parameters: - "$ref": "#/parameters/stats" + - "$ref": "#/parameters/snapshot" + - "$ref": "#/parameters/healthcheck" summary: "display all vip:port:proto and rsip:port list" responses: '200': description: Success schema: "$ref": "#/definitions/VirtualServerList" - # items: - # "$ref": "#/definitions/VirtualServer" + '204': + description: No Content + x-go-name: NoContent + schema: + "$ref": "#/definitions/VirtualServerList" /vs/{VipPort}: get: tags: - - "virtualserver" + - "virtualserver" parameters: + - "$ref": "#/parameters/snapshot" + - "$ref": "#/parameters/healthcheck" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/stats" summary: "get a specific virtual server" @@ -945,11 +1134,9 @@ paths: '200': description: Success schema: - # type: string - # items: "$ref": "#/definitions/VirtualServerList" '404': - description: Service not found + description: Service not found schema: type: string delete: @@ -977,6 +1164,7 @@ paths: tags: - "virtualserver" parameters: + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/vs-config" responses: @@ -992,7 +1180,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1006,9 +1194,9 @@ paths: /vs/{VipPort}/laddr: get: tags: - - "virtualserver" + - "virtualserver" parameters: - #- "$ref": "#/parameters/realserver-id" + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/stats" responses: @@ -1016,17 +1204,15 @@ paths: description: Success schema: "$ref": "#/definitions/LocalAddressExpandList" - #type: string - #items: - # "$ref": "#/definitions/VirtualServer" '404': description: Service not found schema: type: string put: tags: - - "virtualserver" + - "virtualserver" parameters: + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/laddr-config" responses: @@ -1042,7 +1228,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1055,7 +1241,7 @@ paths: "$ref": "#/definitions/Error" delete: tags: - - "virtualserver" + - "virtualserver" parameters: - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/laddr-config" @@ -1068,7 +1254,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1079,12 +1265,46 @@ paths: x-go-name: Failure schema: "$ref": "#/definitions/Error" + /vs/{VipPort}/rs/health: + put: + summary: "dpvs healthcheck update rs weight" + tags: + - "virtualserver" + parameters: + - "$ref": "#/parameters/version" + - "$ref": "#/parameters/service-id" + - "$ref": "#/parameters/rss-config" + responses: + '200': + description: Success + '270': + description: > + the rss-config parameter is outdated, + update nothing and return the latest rs info + x-go-name: Unexpected + schema: + "$ref": "#/definitions/VirtualServerSpecExpand" + '460': + description: Invalid frontend in service configuration + x-go-name: InvalidFrontend + schema: + "$ref": "#/definitions/Error" + '461': + description: Invalid backend in service configuration + x-go-name: InvalidBackend + schema: + "$ref": "#/definitions/Error" + '500': + description: Service deletion failed + x-go-name: Failure + schema: + "$ref": "#/definitions/Error" /vs/{VipPort}/rs: get: tags: - - "virtualserver" + - "virtualserver" parameters: - #- "$ref": "#/parameters/realserver-id" + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/stats" responses: @@ -1092,10 +1312,8 @@ paths: description: Success schema: type: string - #items: - # "$ref": "#/definitions/VirtualServer" '404': - description: Service not found + description: Service not found schema: type: string delete: @@ -1116,7 +1334,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1134,7 +1352,6 @@ paths: parameters: - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/rss-config" - - "$ref": "#/parameters/healthcheck" responses: '200': description: Success @@ -1148,7 +1365,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1164,6 +1381,7 @@ paths: tags: - "virtualserver" parameters: + - "$ref": "#/parameters/snapshot" - "$ref": "#/parameters/service-id" - "$ref": "#/parameters/rss-config" responses: @@ -1179,7 +1397,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1193,19 +1411,16 @@ paths: /vs/{VipPort}/deny: get: tags: - - "virtualserver" + - "virtualserver" parameters: - #- "$ref": "#/parameters/realserver-id" - "$ref": "#/parameters/service-id" responses: '200': description: Success schema: type: string - #items: - # "$ref": "#/definitions/VirtualServer" '404': - description: Service not found + description: Service not found schema: type: string delete: @@ -1226,7 +1441,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1257,7 +1472,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1288,7 +1503,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1302,19 +1517,16 @@ paths: /vs/{VipPort}/allow: get: tags: - - "virtualserver" + - "virtualserver" parameters: - #- "$ref": "#/parameters/realserver-id" - "$ref": "#/parameters/service-id" responses: '200': description: Success schema: type: string - #items: - # "$ref": "#/definitions/VirtualServer" '404': - description: Service not found + description: Service not found schema: type: string delete: @@ -1335,7 +1547,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1366,7 +1578,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1397,7 +1609,7 @@ paths: description: Invalid frontend in service configuration x-go-name: InvalidFrontend schema: - "$ref": "#/definitions/Error" + "$ref": "#/definitions/Error" '461': description: Invalid backend in service configuration x-go-name: InvalidBackend @@ -1408,3 +1620,209 @@ paths: x-go-name: Failure schema: "$ref": "#/definitions/Error" + /ipset: + get: + summary: "Get all the ipsets and members." + tags: + - "ipset" + operationId: GetAll + responses: + '200': + description: Success + schema: + $ref: "#/definitions/IpsetInfoArray" + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + /ipset/{name}: + get: + summary: "Get a specific ipset and its members." + tags: + - "ipset" + operationId: Get + parameters: + - "$ref": "#/parameters/ipset-name" + responses: + '200': + description: Success + schema: + $ref: "#/definitions/IpsetInfo" + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + put: + summary: "Create an ipset named {name}." + tags: + - "ipset" + operationId: Create + parameters: + - $ref: "#/parameters/ipset-name" + - $ref: "#/parameters/ipset-param" + responses: + '200': + description: Replaced + schema: + type: string + '201': + description: Created + schema: + type: string + '400': + description: Invalid ipset parameter + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + delete: + summary: "Delete the ipset named {name}." + tags: + - "ipset" + operationId: Destroy + parameters: + - $ref: "#/parameters/ipset-name" + responses: + '200': + description: Deleted + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + /ipset/{name}/cell: + post: + summary: "Check if an object in the ipset." + tags: + - "ipset" + operationId: IsIn + parameters: + - $ref: "#/parameters/ipset-name" + - $ref: "#/parameters/ipset-cell" + responses: + '200': + description: Succeed + schema: + type: object + required: + - Result + properties: + Result: + type: boolean + Message: + type: string + '400': + description: Invalid ipset parameter + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + /ipset/{name}/member: + post: + summary: "Add members to the ipset." + tags: + - "ipset" + operationId: AddMember + parameters: + - $ref: "#/parameters/ipset-name" + - $ref: "#/parameters/ipset-param" + responses: + '200': + description: Existed + schema: + type: string + '201': + description: Created + schema: + type: string + '400': + description: Invalid ipset parameter + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + delete: + summary: "Delete members from the ipset." + tags: + - "ipset" + operationId: DelMember + parameters: + - $ref: "#/parameters/ipset-name" + - $ref: "#/parameters/ipset-param" + responses: + '200': + description: Succeed + schema: + type: string + '400': + description: Invalid ipset parameter + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string + put: + summary: "Reset the whole ipset members." + tags: + - "ipset" + operationId: ReplaceMember + parameters: + - $ref: "#/parameters/ipset-name" + - $ref: "#/parameters/ipset-param" + responses: + '200': + description: Succeed + schema: + type: string + '400': + description: Invalid ipset parameter + schema: + type: string + '404': + description: Ipset not found + schema: + type: string + '500': + description: Service not available + x-go-name: Failure + schema: + type: string diff --git a/tools/dpvs-agent/go.mod b/tools/dpvs-agent/go.mod index b5198bfc6..2ca083f5e 100644 --- a/tools/dpvs-agent/go.mod +++ b/tools/dpvs-agent/go.mod @@ -14,8 +14,8 @@ require ( github.com/jessevdk/go-flags v1.5.0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/vishvananda/netlink v1.1.0 - golang.org/x/net v0.10.0 - golang.org/x/sys v0.8.0 + golang.org/x/net v0.17.0 + golang.org/x/sys v0.13.0 ) require ( diff --git a/tools/dpvs-agent/go.sum b/tools/dpvs-agent/go.sum index f6f0a32b6..72379d46b 100644 --- a/tools/dpvs-agent/go.sum +++ b/tools/dpvs-agent/go.sum @@ -177,8 +177,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -202,8 +202,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/tools/dpvs-agent/models/cert_auth_spec.go b/tools/dpvs-agent/models/cert_auth_spec.go index 61648012e..a7896bd28 100644 --- a/tools/dpvs-agent/models/cert_auth_spec.go +++ b/tools/dpvs-agent/models/cert_auth_spec.go @@ -19,6 +19,9 @@ type CertAuthSpec struct { // addr Addr string `json:"addr,omitempty"` + + // ipset + Ipset string `json:"ipset,omitempty"` } // Validate validates this cert auth spec diff --git a/tools/dpvs-agent/models/dest_check_spec.go b/tools/dpvs-agent/models/dest_check_spec.go index 2e2e9cf9a..a2870aa5e 100644 --- a/tools/dpvs-agent/models/dest_check_spec.go +++ b/tools/dpvs-agent/models/dest_check_spec.go @@ -41,6 +41,12 @@ const ( // DestCheckSpecPing captures enum value "ping" DestCheckSpecPing DestCheckSpec = "ping" + + // DestCheckSpecUdpping captures enum value "udpping" + DestCheckSpecUdpping DestCheckSpec = "udpping" + + // DestCheckSpecHTTP captures enum value "http" + DestCheckSpecHTTP DestCheckSpec = "http" ) // for schema @@ -48,7 +54,7 @@ var destCheckSpecEnum []interface{} func init() { var res []DestCheckSpec - if err := json.Unmarshal([]byte(`["passive","tcp","udp","ping"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["passive","tcp","udp","ping","udpping","http"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/tools/dpvs-agent/models/dpvs_node_spec.go b/tools/dpvs-agent/models/dpvs_node_spec.go new file mode 100644 index 000000000..c8660bfaa --- /dev/null +++ b/tools/dpvs-agent/models/dpvs_node_spec.go @@ -0,0 +1,150 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// DpvsNodeSpec dpvs node spec +// +// swagger:model DpvsNodeSpec +type DpvsNodeSpec struct { + + // announce port + AnnouncePort *VsAnnouncePort `json:"AnnouncePort,omitempty"` + + // laddrs + Laddrs *LocalAddressExpandList `json:"Laddrs,omitempty"` +} + +// Validate validates this dpvs node spec +func (m *DpvsNodeSpec) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAnnouncePort(formats); err != nil { + res = append(res, err) + } + + if err := m.validateLaddrs(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *DpvsNodeSpec) validateAnnouncePort(formats strfmt.Registry) error { + if swag.IsZero(m.AnnouncePort) { // not required + return nil + } + + if m.AnnouncePort != nil { + if err := m.AnnouncePort.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("AnnouncePort") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("AnnouncePort") + } + return err + } + } + + return nil +} + +func (m *DpvsNodeSpec) validateLaddrs(formats strfmt.Registry) error { + if swag.IsZero(m.Laddrs) { // not required + return nil + } + + if m.Laddrs != nil { + if err := m.Laddrs.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Laddrs") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Laddrs") + } + return err + } + } + + return nil +} + +// ContextValidate validate this dpvs node spec based on the context it is used +func (m *DpvsNodeSpec) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateAnnouncePort(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateLaddrs(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *DpvsNodeSpec) contextValidateAnnouncePort(ctx context.Context, formats strfmt.Registry) error { + + if m.AnnouncePort != nil { + if err := m.AnnouncePort.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("AnnouncePort") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("AnnouncePort") + } + return err + } + } + + return nil +} + +func (m *DpvsNodeSpec) contextValidateLaddrs(ctx context.Context, formats strfmt.Registry) error { + + if m.Laddrs != nil { + if err := m.Laddrs.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Laddrs") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Laddrs") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *DpvsNodeSpec) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *DpvsNodeSpec) UnmarshalBinary(b []byte) error { + var res DpvsNodeSpec + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_cell.go b/tools/dpvs-agent/models/ipset_cell.go new file mode 100644 index 000000000..280262d27 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_cell.go @@ -0,0 +1,159 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IpsetCell IpsetCell represents an indivisible granularity of ipset member. +// +// swagger:model IpsetCell +type IpsetCell struct { + + // member + // Required: true + Member *IpsetMember `json:"Member"` + + // type + // Required: true + Type *IpsetType `json:"Type"` +} + +// Validate validates this ipset cell +func (m *IpsetCell) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateMember(formats); err != nil { + res = append(res, err) + } + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetCell) validateMember(formats strfmt.Registry) error { + + if err := validate.Required("Member", "body", m.Member); err != nil { + return err + } + + if m.Member != nil { + if err := m.Member.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Member") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Member") + } + return err + } + } + + return nil +} + +func (m *IpsetCell) validateType(formats strfmt.Registry) error { + + if err := validate.Required("Type", "body", m.Type); err != nil { + return err + } + + if err := validate.Required("Type", "body", m.Type); err != nil { + return err + } + + if m.Type != nil { + if err := m.Type.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Type") + } + return err + } + } + + return nil +} + +// ContextValidate validate this ipset cell based on the context it is used +func (m *IpsetCell) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateMember(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateType(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetCell) contextValidateMember(ctx context.Context, formats strfmt.Registry) error { + + if m.Member != nil { + if err := m.Member.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Member") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Member") + } + return err + } + } + + return nil +} + +func (m *IpsetCell) contextValidateType(ctx context.Context, formats strfmt.Registry) error { + + if m.Type != nil { + if err := m.Type.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Type") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetCell) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetCell) UnmarshalBinary(b []byte) error { + var res IpsetCell + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_creation_option.go b/tools/dpvs-agent/models/ipset_creation_option.go new file mode 100644 index 000000000..ff176fb85 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_creation_option.go @@ -0,0 +1,118 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IpsetCreationOption IpsetCreationOption contains all available options required in creating an ipset. +// +// swagger:model IpsetCreationOption +type IpsetCreationOption struct { + + // comment + Comment *bool `json:"Comment,omitempty"` + + // family + // Enum: [ipv4 ipv6] + Family string `json:"Family,omitempty"` + + // hash max elem + HashMaxElem uint32 `json:"HashMaxElem,omitempty"` + + // hash size + HashSize uint32 `json:"HashSize,omitempty"` + + // vaild format: ipv4-ipv4, ipv4/pfx, ipv6/pfx, port-port + // + Range string `json:"Range,omitempty"` +} + +// Validate validates this ipset creation option +func (m *IpsetCreationOption) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateFamily(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var ipsetCreationOptionTypeFamilyPropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["ipv4","ipv6"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + ipsetCreationOptionTypeFamilyPropEnum = append(ipsetCreationOptionTypeFamilyPropEnum, v) + } +} + +const ( + + // IpsetCreationOptionFamilyIPV4 captures enum value "ipv4" + IpsetCreationOptionFamilyIPV4 string = "ipv4" + + // IpsetCreationOptionFamilyIPV6 captures enum value "ipv6" + IpsetCreationOptionFamilyIPV6 string = "ipv6" +) + +// prop value enum +func (m *IpsetCreationOption) validateFamilyEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, ipsetCreationOptionTypeFamilyPropEnum, true); err != nil { + return err + } + return nil +} + +func (m *IpsetCreationOption) validateFamily(formats strfmt.Registry) error { + if swag.IsZero(m.Family) { // not required + return nil + } + + // value enum + if err := m.validateFamilyEnum("Family", "body", m.Family); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this ipset creation option based on context it is used +func (m *IpsetCreationOption) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetCreationOption) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetCreationOption) UnmarshalBinary(b []byte) error { + var res IpsetCreationOption + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_info.go b/tools/dpvs-agent/models/ipset_info.go new file mode 100644 index 000000000..39e6b1e2b --- /dev/null +++ b/tools/dpvs-agent/models/ipset_info.go @@ -0,0 +1,284 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IpsetInfo IpsetInfo contains all parameters and information for ipset operations. +// +// swagger:model IpsetInfo +type IpsetInfo struct { + + // creation options + CreationOptions *IpsetCreationOption `json:"CreationOptions,omitempty"` + + // entries + Entries []*IpsetMember `json:"Entries"` + + // name + // Required: true + // Max Length: 32 + // Min Length: 1 + Name *string `json:"Name"` + + // opertaion type code + // Enum: [1 2 3 4 5 6 7] + Opcode uint16 `json:"Opcode,omitempty"` + + // type + // Required: true + Type *IpsetType `json:"Type"` +} + +// Validate validates this ipset info +func (m *IpsetInfo) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateCreationOptions(formats); err != nil { + res = append(res, err) + } + + if err := m.validateEntries(formats); err != nil { + res = append(res, err) + } + + if err := m.validateName(formats); err != nil { + res = append(res, err) + } + + if err := m.validateOpcode(formats); err != nil { + res = append(res, err) + } + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetInfo) validateCreationOptions(formats strfmt.Registry) error { + if swag.IsZero(m.CreationOptions) { // not required + return nil + } + + if m.CreationOptions != nil { + if err := m.CreationOptions.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("CreationOptions") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("CreationOptions") + } + return err + } + } + + return nil +} + +func (m *IpsetInfo) validateEntries(formats strfmt.Registry) error { + if swag.IsZero(m.Entries) { // not required + return nil + } + + for i := 0; i < len(m.Entries); i++ { + if swag.IsZero(m.Entries[i]) { // not required + continue + } + + if m.Entries[i] != nil { + if err := m.Entries[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Entries" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Entries" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *IpsetInfo) validateName(formats strfmt.Registry) error { + + if err := validate.Required("Name", "body", m.Name); err != nil { + return err + } + + if err := validate.MinLength("Name", "body", *m.Name, 1); err != nil { + return err + } + + if err := validate.MaxLength("Name", "body", *m.Name, 32); err != nil { + return err + } + + return nil +} + +var ipsetInfoTypeOpcodePropEnum []interface{} + +func init() { + var res []uint16 + if err := json.Unmarshal([]byte(`[1,2,3,4,5,6,7]`), &res); err != nil { + panic(err) + } + for _, v := range res { + ipsetInfoTypeOpcodePropEnum = append(ipsetInfoTypeOpcodePropEnum, v) + } +} + +// prop value enum +func (m *IpsetInfo) validateOpcodeEnum(path, location string, value uint16) error { + if err := validate.EnumCase(path, location, value, ipsetInfoTypeOpcodePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *IpsetInfo) validateOpcode(formats strfmt.Registry) error { + if swag.IsZero(m.Opcode) { // not required + return nil + } + + // value enum + if err := m.validateOpcodeEnum("Opcode", "body", m.Opcode); err != nil { + return err + } + + return nil +} + +func (m *IpsetInfo) validateType(formats strfmt.Registry) error { + + if err := validate.Required("Type", "body", m.Type); err != nil { + return err + } + + if err := validate.Required("Type", "body", m.Type); err != nil { + return err + } + + if m.Type != nil { + if err := m.Type.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Type") + } + return err + } + } + + return nil +} + +// ContextValidate validate this ipset info based on the context it is used +func (m *IpsetInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateCreationOptions(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateEntries(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateType(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetInfo) contextValidateCreationOptions(ctx context.Context, formats strfmt.Registry) error { + + if m.CreationOptions != nil { + if err := m.CreationOptions.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("CreationOptions") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("CreationOptions") + } + return err + } + } + + return nil +} + +func (m *IpsetInfo) contextValidateEntries(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Entries); i++ { + + if m.Entries[i] != nil { + if err := m.Entries[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Entries" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Entries" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *IpsetInfo) contextValidateType(ctx context.Context, formats strfmt.Registry) error { + + if m.Type != nil { + if err := m.Type.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Type") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetInfo) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetInfo) UnmarshalBinary(b []byte) error { + var res IpsetInfo + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_info_array.go b/tools/dpvs-agent/models/ipset_info_array.go new file mode 100644 index 000000000..432e65ff3 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_info_array.go @@ -0,0 +1,119 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// IpsetInfoArray IpsetInfoArray contains an array of ipset. +// +// swagger:model IpsetInfoArray +type IpsetInfoArray struct { + + // count + Count int32 `json:"Count,omitempty"` + + // infos + Infos []*IpsetInfo `json:"Infos"` +} + +// Validate validates this ipset info array +func (m *IpsetInfoArray) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateInfos(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetInfoArray) validateInfos(formats strfmt.Registry) error { + if swag.IsZero(m.Infos) { // not required + return nil + } + + for i := 0; i < len(m.Infos); i++ { + if swag.IsZero(m.Infos[i]) { // not required + continue + } + + if m.Infos[i] != nil { + if err := m.Infos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Infos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Infos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this ipset info array based on the context it is used +func (m *IpsetInfoArray) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateInfos(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetInfoArray) contextValidateInfos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Infos); i++ { + + if m.Infos[i] != nil { + if err := m.Infos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Infos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Infos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetInfoArray) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetInfoArray) UnmarshalBinary(b []byte) error { + var res IpsetInfoArray + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_member.go b/tools/dpvs-agent/models/ipset_member.go new file mode 100644 index 000000000..c1ac8bab2 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_member.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IpsetMember IpsetMember represents a specific entry in ipset. +// +// swagger:model IpsetMember +type IpsetMember struct { + + // comment + // Max Length: 32 + // Min Length: 1 + Comment string `json:"Comment,omitempty"` + + // type specific entry data, for example + // * 192.168.1.0/29 (bitmap:ip) + // * 192.168.88.0/24,tcp:8080-8082 (hash:net) + // * 2001::1,8080-8082,2002::aaaa:bbbb:ccc0:0/108 (hash:ip,port,net) + // + // Required: true + Entry *string `json:"Entry"` + + // options + Options *IpsetOption `json:"Options,omitempty"` +} + +// Validate validates this ipset member +func (m *IpsetMember) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateComment(formats); err != nil { + res = append(res, err) + } + + if err := m.validateEntry(formats); err != nil { + res = append(res, err) + } + + if err := m.validateOptions(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetMember) validateComment(formats strfmt.Registry) error { + if swag.IsZero(m.Comment) { // not required + return nil + } + + if err := validate.MinLength("Comment", "body", m.Comment, 1); err != nil { + return err + } + + if err := validate.MaxLength("Comment", "body", m.Comment, 32); err != nil { + return err + } + + return nil +} + +func (m *IpsetMember) validateEntry(formats strfmt.Registry) error { + + if err := validate.Required("Entry", "body", m.Entry); err != nil { + return err + } + + return nil +} + +func (m *IpsetMember) validateOptions(formats strfmt.Registry) error { + if swag.IsZero(m.Options) { // not required + return nil + } + + if m.Options != nil { + if err := m.Options.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Options") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Options") + } + return err + } + } + + return nil +} + +// ContextValidate validate this ipset member based on the context it is used +func (m *IpsetMember) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateOptions(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IpsetMember) contextValidateOptions(ctx context.Context, formats strfmt.Registry) error { + + if m.Options != nil { + if err := m.Options.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Options") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Options") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetMember) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetMember) UnmarshalBinary(b []byte) error { + var res IpsetMember + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_option.go b/tools/dpvs-agent/models/ipset_option.go new file mode 100644 index 000000000..a250729c0 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_option.go @@ -0,0 +1,55 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// IpsetOption IpsetOption defines common options for ipset operations. +// +// swagger:model IpsetOption +type IpsetOption struct { + + // When add members to ipset with Force set, the already existing members are replaced sliently instead of emitting an EDPVS_EXIST error; When delete non-existent memebers from ipset, the DPSVS_NOTEXIST error is ignored. + // + Force *bool `json:"Force,omitempty"` + + // Nomatch excludes a small element range from an ipset, which is mainly used by network-cidr based ipset. + // + NoMatch *bool `json:"NoMatch,omitempty"` +} + +// Validate validates this ipset option +func (m *IpsetOption) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this ipset option based on context it is used +func (m *IpsetOption) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *IpsetOption) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IpsetOption) UnmarshalBinary(b []byte) error { + var res IpsetOption + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/ipset_type.go b/tools/dpvs-agent/models/ipset_type.go new file mode 100644 index 000000000..ecf655aa8 --- /dev/null +++ b/tools/dpvs-agent/models/ipset_type.go @@ -0,0 +1,108 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// IpsetType ipset type +// +// swagger:model IpsetType +type IpsetType string + +func NewIpsetType(value IpsetType) *IpsetType { + return &value +} + +// Pointer returns a pointer to a freshly-allocated IpsetType. +func (m IpsetType) Pointer() *IpsetType { + return &m +} + +const ( + + // IpsetTypeBitmapIP captures enum value "bitmap:ip" + IpsetTypeBitmapIP IpsetType = "bitmap:ip" + + // IpsetTypeBitmapIPMac captures enum value "bitmap:ip,mac" + IpsetTypeBitmapIPMac IpsetType = "bitmap:ip,mac" + + // IpsetTypeBitmapPort captures enum value "bitmap:port" + IpsetTypeBitmapPort IpsetType = "bitmap:port" + + // IpsetTypeHashIP captures enum value "hash:ip" + IpsetTypeHashIP IpsetType = "hash:ip" + + // IpsetTypeHashNet captures enum value "hash:net" + IpsetTypeHashNet IpsetType = "hash:net" + + // IpsetTypeHashIPPort captures enum value "hash:ip,port" + IpsetTypeHashIPPort IpsetType = "hash:ip,port" + + // IpsetTypeHashNetPort captures enum value "hash:net,port" + IpsetTypeHashNetPort IpsetType = "hash:net,port" + + // IpsetTypeHashNetPortIface captures enum value "hash:net,port,iface" + IpsetTypeHashNetPortIface IpsetType = "hash:net,port,iface" + + // IpsetTypeHashIPPortIP captures enum value "hash:ip,port,ip" + IpsetTypeHashIPPortIP IpsetType = "hash:ip,port,ip" + + // IpsetTypeHashIPPortNet captures enum value "hash:ip,port,net" + IpsetTypeHashIPPortNet IpsetType = "hash:ip,port,net" + + // IpsetTypeHashNetPortNet captures enum value "hash:net,port,net" + IpsetTypeHashNetPortNet IpsetType = "hash:net,port,net" + + // IpsetTypeHashNetPortNetPort captures enum value "hash:net,port,net,port" + IpsetTypeHashNetPortNetPort IpsetType = "hash:net,port,net,port" +) + +// for schema +var ipsetTypeEnum []interface{} + +func init() { + var res []IpsetType + if err := json.Unmarshal([]byte(`["bitmap:ip","bitmap:ip,mac","bitmap:port","hash:ip","hash:net","hash:ip,port","hash:net,port","hash:net,port,iface","hash:ip,port,ip","hash:ip,port,net","hash:net,port,net","hash:net,port,net,port"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + ipsetTypeEnum = append(ipsetTypeEnum, v) + } +} + +func (m IpsetType) validateIpsetTypeEnum(path, location string, value IpsetType) error { + if err := validate.EnumCase(path, location, value, ipsetTypeEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this ipset type +func (m IpsetType) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateIpsetTypeEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validates this ipset type based on context it is used +func (m IpsetType) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/tools/dpvs-agent/models/node_service_snapshot.go b/tools/dpvs-agent/models/node_service_snapshot.go new file mode 100644 index 000000000..148038e75 --- /dev/null +++ b/tools/dpvs-agent/models/node_service_snapshot.go @@ -0,0 +1,150 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NodeServiceSnapshot node service snapshot +// +// swagger:model NodeServiceSnapshot +type NodeServiceSnapshot struct { + + // node spec + NodeSpec *DpvsNodeSpec `json:"NodeSpec,omitempty"` + + // services + Services *VirtualServerList `json:"Services,omitempty"` +} + +// Validate validates this node service snapshot +func (m *NodeServiceSnapshot) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateNodeSpec(formats); err != nil { + res = append(res, err) + } + + if err := m.validateServices(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *NodeServiceSnapshot) validateNodeSpec(formats strfmt.Registry) error { + if swag.IsZero(m.NodeSpec) { // not required + return nil + } + + if m.NodeSpec != nil { + if err := m.NodeSpec.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("NodeSpec") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("NodeSpec") + } + return err + } + } + + return nil +} + +func (m *NodeServiceSnapshot) validateServices(formats strfmt.Registry) error { + if swag.IsZero(m.Services) { // not required + return nil + } + + if m.Services != nil { + if err := m.Services.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Services") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Services") + } + return err + } + } + + return nil +} + +// ContextValidate validate this node service snapshot based on the context it is used +func (m *NodeServiceSnapshot) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateNodeSpec(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateServices(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *NodeServiceSnapshot) contextValidateNodeSpec(ctx context.Context, formats strfmt.Registry) error { + + if m.NodeSpec != nil { + if err := m.NodeSpec.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("NodeSpec") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("NodeSpec") + } + return err + } + } + + return nil +} + +func (m *NodeServiceSnapshot) contextValidateServices(ctx context.Context, formats strfmt.Registry) error { + + if m.Services != nil { + if err := m.Services.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("Services") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("Services") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *NodeServiceSnapshot) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NodeServiceSnapshot) UnmarshalBinary(b []byte) error { + var res NodeServiceSnapshot + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/models/virtual_server_spec_expand.go b/tools/dpvs-agent/models/virtual_server_spec_expand.go index fbd31358d..21482cc6e 100644 --- a/tools/dpvs-agent/models/virtual_server_spec_expand.go +++ b/tools/dpvs-agent/models/virtual_server_spec_expand.go @@ -70,6 +70,10 @@ type VirtualServerSpecExpand struct { // Enum: [0 1 2 17 18] ProxyProto uint8 `json:"ProxyProto,omitempty"` + // quic + // Enum: [true false] + Quic string `json:"Quic,omitempty"` + // r ss RSs *RealServerExpandList `json:"RSs,omitempty"` @@ -86,6 +90,9 @@ type VirtualServerSpecExpand struct { // timeout Timeout uint32 `json:"Timeout,omitempty"` + + // version + Version string `json:"Version,omitempty"` } // Validate validates this virtual server spec expand @@ -108,6 +115,10 @@ func (m *VirtualServerSpecExpand) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateQuic(formats); err != nil { + res = append(res, err) + } + if err := m.validateRSs(formats); err != nil { res = append(res, err) } @@ -245,6 +256,48 @@ func (m *VirtualServerSpecExpand) validateProxyProto(formats strfmt.Registry) er return nil } +var virtualServerSpecExpandTypeQuicPropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["true","false"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + virtualServerSpecExpandTypeQuicPropEnum = append(virtualServerSpecExpandTypeQuicPropEnum, v) + } +} + +const ( + + // VirtualServerSpecExpandQuicTrue captures enum value "true" + VirtualServerSpecExpandQuicTrue string = "true" + + // VirtualServerSpecExpandQuicFalse captures enum value "false" + VirtualServerSpecExpandQuicFalse string = "false" +) + +// prop value enum +func (m *VirtualServerSpecExpand) validateQuicEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, virtualServerSpecExpandTypeQuicPropEnum, true); err != nil { + return err + } + return nil +} + +func (m *VirtualServerSpecExpand) validateQuic(formats strfmt.Registry) error { + if swag.IsZero(m.Quic) { // not required + return nil + } + + // value enum + if err := m.validateQuicEnum("Quic", "body", m.Quic); err != nil { + return err + } + + return nil +} + func (m *VirtualServerSpecExpand) validateRSs(formats strfmt.Registry) error { if swag.IsZero(m.RSs) { // not required return nil diff --git a/tools/dpvs-agent/models/virtual_server_spec_tiny.go b/tools/dpvs-agent/models/virtual_server_spec_tiny.go index 0219c186d..543a417fa 100644 --- a/tools/dpvs-agent/models/virtual_server_spec_tiny.go +++ b/tools/dpvs-agent/models/virtual_server_spec_tiny.go @@ -42,6 +42,9 @@ type VirtualServerSpecTiny struct { // Enum: [v2 v2-insecure v1 v1-insecure disable] ProxyProtocol string `json:"ProxyProtocol,omitempty"` + // quic + Quic *bool `json:"Quic,omitempty"` + // sched name // Enum: [rr wrr wlc conhash] SchedName string `json:"SchedName,omitempty"` diff --git a/tools/dpvs-agent/models/vs_announce_port.go b/tools/dpvs-agent/models/vs_announce_port.go new file mode 100644 index 000000000..8445066d8 --- /dev/null +++ b/tools/dpvs-agent/models/vs_announce_port.go @@ -0,0 +1,53 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// VsAnnouncePort vs announce port +// +// swagger:model VsAnnouncePort +type VsAnnouncePort struct { + + // dpvs + Dpvs string `json:"dpvs,omitempty"` + + // switch + Switch string `json:"switch,omitempty"` +} + +// Validate validates this vs announce port +func (m *VsAnnouncePort) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this vs announce port based on context it is used +func (m *VsAnnouncePort) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *VsAnnouncePort) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *VsAnnouncePort) UnmarshalBinary(b []byte) error { + var res VsAnnouncePort + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/tools/dpvs-agent/pkg/ipc/types/certificate.go b/tools/dpvs-agent/pkg/ipc/types/certificate.go index 5f8ea82a5..7916bd897 100644 --- a/tools/dpvs-agent/pkg/ipc/types/certificate.go +++ b/tools/dpvs-agent/pkg/ipc/types/certificate.go @@ -31,14 +31,19 @@ import ( "github.com/dpvs-agent/pkg/ipc/pool" ) +/* +derived from: + - include/conf/blklst.h + - include/conf/whtlst.h +*/ type CertificateAuthoritySpec struct { - src [0x10]byte - dst [0x10]byte - af uint32 - fwmark uint32 - port uint16 - proto uint8 - padding uint8 + vaddr [0x10]byte + vport uint16 + proto uint8 + af uint8 + + caddr [0x10]byte + ipset [IPSET_MAXNAMELEN]byte } type CertificateAuthorityFront struct { @@ -54,12 +59,12 @@ func NewCertificateAuthorityFront() *CertificateAuthorityFront { } func (o *CertificateAuthoritySpec) Copy(src *CertificateAuthoritySpec) bool { - o.af = src.af - o.fwmark = src.fwmark - o.port = src.port + copy(o.vaddr[:], src.vaddr[:]) + o.vport = src.vport o.proto = src.proto - copy(o.src[:], src.src[:]) - copy(o.dst[:], src.dst[:]) + o.af = src.af + copy(o.caddr[:], src.caddr[:]) + copy(o.ipset[:], src.ipset[:]) return true } @@ -80,19 +85,19 @@ func (o *CertificateAuthoritySpec) ParseVipPortProto(vipport string) error { o.proto = unix.IPPROTO_TCP } - // port := items[1] - port, err := strconv.Atoi(items[1]) + // vport := items[1] + vport, err := strconv.Atoi(items[1]) if err != nil { return err } - o.SetPort(uint16(port)) + o.SetVport(uint16(vport)) - vip := items[0] - if net.ParseIP(vip) == nil { - return errors.New(fmt.Sprintf("invalid ip addr: %s\n", vip)) + vaddr := items[0] + if net.ParseIP(vaddr) == nil { + return errors.New(fmt.Sprintf("invalid ip addr: %s\n", vaddr)) } - o.SetDst(vip) + o.SetVaddr(vaddr) return nil } @@ -150,42 +155,53 @@ func (o *CertificateAuthorityFront) GetCount() uint32 { return o.count } -func (o *CertificateAuthoritySpec) SetAf(af uint32) { +func (o *CertificateAuthoritySpec) SetAf(af uint8) { o.af = af } -func (o *CertificateAuthoritySpec) SetSrc(addr string) { +func (o *CertificateAuthoritySpec) SetCaddr(addr string) { + if len(addr) == 0 { + var zeros [0x10]byte + copy(o.caddr[:], zeros[:]) + return + } if strings.Contains(addr, ":") { o.SetAf(unix.AF_INET6) - copy(o.src[:], net.ParseIP(addr)) + copy(o.caddr[:], net.ParseIP(addr)) return } o.SetAf(unix.AF_INET) buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, net.ParseIP(addr)) - copy(o.src[:], buf.Bytes()[12:]) + copy(o.caddr[:], buf.Bytes()[12:]) } -func (o *CertificateAuthoritySpec) SetDst(addr string) { +func (o *CertificateAuthoritySpec) SetVaddr(addr string) { if strings.Contains(addr, ":") { o.SetAf(unix.AF_INET6) - copy(o.dst[:], net.ParseIP(addr)) + copy(o.vaddr[:], net.ParseIP(addr)) return } o.SetAf(unix.AF_INET) buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, net.ParseIP(addr)) - copy(o.dst[:], buf.Bytes()[12:]) + copy(o.vaddr[:], buf.Bytes()[12:]) } -func (o *CertificateAuthoritySpec) SetFwmark(fwmark uint32) { - o.fwmark = fwmark +func (o *CertificateAuthoritySpec) SetIpset(ipset string) { + if len(ipset) == 0 { + var zeros [IPSET_MAXNAMELEN]byte + copy(o.ipset[:], zeros[:]) + return + } + buf := []byte(ipset) + copy(o.ipset[:], buf[6:]) } -func (o *CertificateAuthoritySpec) SetPort(port uint16) { +func (o *CertificateAuthoritySpec) SetVport(port uint16) { buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint16(port)) - o.port = binary.BigEndian.Uint16(buf.Bytes()) + o.vport = binary.BigEndian.Uint16(buf.Bytes()) } func (o *CertificateAuthoritySpec) SetProto(proto string) { diff --git a/tools/dpvs-agent/pkg/ipc/types/const.go b/tools/dpvs-agent/pkg/ipc/types/const.go index 6cbf89f02..ade0c16c8 100644 --- a/tools/dpvs-agent/pkg/ipc/types/const.go +++ b/tools/dpvs-agent/pkg/ipc/types/const.go @@ -47,6 +47,7 @@ const ( DPVS_SVC_F_SIP_HASH // 0x100 DPVS_SVC_F_QID_HASH // 0x200 DPVS_SVC_F_MATCH // 0x400 + DPVS_SVC_F_QUIC // 0x800 ) const ( @@ -240,6 +241,7 @@ const ( SOCKOPT_SET_IFADDR_SET SOCKOPT_SET_IFADDR_FLUSH SOCKOPT_GET_IFADDR_SHOW + SOCKOPT_GET_IFMADDR_SHOW SOCKOPT_NETIF_SET_LCORE SOCKOPT_NETIF_SET_PORT @@ -254,8 +256,12 @@ const ( SOCKOPT_NETIF_GET_PORT_XSTATS SOCKOPT_NETIF_GET_PORT_EXT_INFO SOCKOPT_NETIF_GET_BOND_STATUS + SOCKOPT_NETIF_GET_MADDR SOCKOPT_NETIF_GET_MAX + SOCKOPT_SET_LLDP_TODO + SOCKOPT_GET_LLDP_SHOW + SOCKOPT_SET_NEIGH_ADD SOCKOPT_SET_NEIGH_DEL SOCKOPT_GET_NEIGH_SHOW diff --git a/tools/dpvs-agent/pkg/ipc/types/getmodel.go b/tools/dpvs-agent/pkg/ipc/types/getmodel.go index a1d0e30f8..36be36731 100644 --- a/tools/dpvs-agent/pkg/ipc/types/getmodel.go +++ b/tools/dpvs-agent/pkg/ipc/types/getmodel.go @@ -31,6 +31,7 @@ func (vs *VirtualServerSpec) GetModel() *models.VirtualServerSpecExpand { Fwmark: vs.GetFwmark(), SynProxy: "false", ExpireQuiescent: "false", + Quic: "false", SchedName: vs.GetSchedName(), Timeout: vs.GetTimeout(), Match: vs.match.GetModel(), @@ -49,6 +50,11 @@ func (vs *VirtualServerSpec) GetModel() *models.VirtualServerSpecExpand { flags += "ExpireQuiescent|" } + if (vs.GetFlags() & DPVS_SVC_F_QUIC) != 0 { + modelVs.Quic = "true" + flags += "Quic|" + } + if (vs.GetFlags() & DPVS_SVC_F_QID_HASH) != 0 { flags += "ConHashByQuicID|" } diff --git a/tools/dpvs-agent/pkg/ipc/types/ipset.go b/tools/dpvs-agent/pkg/ipc/types/ipset.go new file mode 100644 index 000000000..eec5d508f --- /dev/null +++ b/tools/dpvs-agent/pkg/ipc/types/ipset.go @@ -0,0 +1,948 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "syscall" + "unsafe" + + "github.com/dpvs-agent/pkg/ipc/pool" + "github.com/hashicorp/go-hclog" + "golang.org/x/sys/unix" +) + +// The consts mirrors const macros defined in conf/ipset.h +const ( + IPSET_MAXNAMELEN = 32 + IPSET_MAXCOMLEN = 32 + + IPSET_F_FORCE = 0x0001 +) + +// The consts mirrors `enum ipset_op` defined in conf/ipset.h +const ( + _ uint16 = iota + IPSET_OP_ADD + IPSET_OP_DEL + IPSET_OP_TEST + IPSET_OP_CREATE + IPSET_OP_DESTROY + IPSET_OP_FLUSH + IPSET_OP_LIST + IPSET_OP_MAX +) + +// InetAddrRange mirrors `struct inet_addr_range` defined in conf/inet.h +type InetAddrRange struct { + minAddr [16]byte + maxAddr [16]byte + minPort uint16 + maxPort uint16 +} + +func (o *InetAddrRange) SetMinAddr(ip net.IP) { + if ip == nil { + return + } + if ip4 := ip.To4(); ip4 != nil { + copy(o.minAddr[:4], ip4[:4]) + } else { + copy(o.minAddr[:], ip[:]) + } +} + +func (o *InetAddrRange) SetMaxAddr(ip net.IP) { + if ip == nil { + return + } + if ip4 := ip.To4(); ip4 != nil { + copy(o.maxAddr[:4], ip4[:4]) + } else { + copy(o.maxAddr[:], ip[:]) + } +} + +func (o *InetAddrRange) SetMinPort(port uint16) { + o.minPort = port +} + +func (o *InetAddrRange) SetMaxPort(port uint16) { + o.maxPort = port +} + +func (o *InetAddrRange) Decode(af uint8) (net.IP, net.IP, uint16, uint16) { + if af == syscall.AF_INET6 { + minAddr := make(net.IP, net.IPv6len) + maxAddr := make(net.IP, net.IPv6len) + copy(minAddr[:], o.minAddr[:16]) + copy(maxAddr[:], o.maxAddr[:16]) + return minAddr, maxAddr, o.minPort, o.maxPort + } else { + minAddr := net.IPv4(o.minAddr[0], o.minAddr[1], o.minAddr[2], o.minAddr[3]) + maxAddr := net.IPv4(o.maxAddr[0], o.maxAddr[1], o.maxAddr[2], o.maxAddr[3]) + return minAddr, maxAddr, o.minPort, o.maxPort + } + return nil, nil, 0, 0 // never hit +} + +func (o *InetAddrRange) Sizeof() uint64 { + return uint64(unsafe.Sizeof(*o)) +} + +func (o *InetAddrRange) Copy(from *InetAddrRange) bool { + if from == nil { + return false + } + copy(o.minAddr[:], from.minAddr[:]) + copy(o.maxAddr[:], from.maxAddr[:]) + o.minPort = from.minPort + o.maxPort = from.maxPort + return true +} + +// IPSetParam mirrors `struct ipset_param` defined in conf/ipset.h +type IPSetParam struct { + kind [IPSET_MAXNAMELEN]byte + name [IPSET_MAXNAMELEN]byte + comment [IPSET_MAXCOMLEN]byte + opcode uint16 + flag uint16 + + // flat reflection of `struct ipset_option`: + // ops create: af(8), comment(8), hashSize(4), maxElem(4) + // ops add: af(8), nomatch(8) + hashSize uint32 + maxElem uint32 + commentOrNomatch uint8 + af uint8 + + proto uint8 + cidr uint8 + addrRange InetAddrRange + iface [unix.IFNAMSIZ]byte + macAddr [6]byte + + // for ipset types with 2 nets + _ uint8 + cidr2 uint8 + addrRange2 InetAddrRange +} + +func (o *IPSetParam) getKind() string { + return string(bytes.TrimRight(o.kind[:], "\x00")) +} + +func (o *IPSetParam) SetKind(kind string) { + if len(kind) > 0 { + copy(o.kind[:], kind) + } +} + +func (o *IPSetParam) SetName(name string) { + if len(name) > 0 { + copy(o.name[:], name) + } +} + +func (o *IPSetParam) SetComment(comment string) { + if len(comment) > 0 { + copy(o.comment[:], comment) + } +} + +func (o *IPSetParam) SetOpcode(opcode uint16) { + o.opcode = opcode +} + +func (o *IPSetParam) SetFlag(flag uint16) { + o.flag = flag +} + +func (o *IPSetParam) AddFlag(flag uint16) { + o.flag |= flag +} + +func (o *IPSetParam) DelFlag(flag uint16) { + o.flag &= ^flag +} + +func (o *IPSetParam) SetAf(af uint8) { + o.af = af +} + +func (o *IPSetParam) SetCommentFlag(enable bool) { + num := 0 + if enable { + num = 1 + } + o.commentOrNomatch = uint8(num) +} + +func (o *IPSetParam) SetNomatch(enable bool) { + num := 0 + if enable { + num = 1 + } + o.commentOrNomatch = uint8(num) +} + +func (o *IPSetParam) SetHashSize(hashSize uint32) { + o.hashSize = hashSize +} + +func (o *IPSetParam) SetMaxElem(maxElem uint32) { + o.maxElem = maxElem +} + +func (o *IPSetParam) SetProto(proto uint8) { + o.proto = proto +} + +func (o *IPSetParam) SetCidr(cidr uint8) { + o.cidr = cidr +} + +func (o *IPSetParam) GetAddrRange() *InetAddrRange { + return &o.addrRange +} + +func (o *IPSetParam) SetIface(iface string) { + if len(iface) > 0 { + copy(o.iface[:], iface) + } +} + +func (o *IPSetParam) SetMacAddr(macAddr string) error { + n, err := fmt.Sscanf(macAddr, "%02x:%02x:%02x:%02x:%02x:%02x", + &o.macAddr[0], &o.macAddr[1], &o.macAddr[2], + &o.macAddr[3], &o.macAddr[4], &o.macAddr[5]) + if err != nil { + return err + } + if n != 6 { + return fmt.Errorf("string macAddr parsed to %d parts, expected 6", n) + } + return nil +} + +func (o *IPSetParam) SetCidr2(cidr uint8) { + o.cidr2 = cidr +} + +func (o *IPSetParam) GetAddrRange2() *InetAddrRange { + return &o.addrRange2 +} + +func (o *IPSetParam) Sizeof() uint64 { + return uint64(unsafe.Sizeof(*o)) +} + +func (o *IPSetParam) Copy(from *IPSetParam) bool { + if from == nil { + return false + } + copy(o.kind[:], from.kind[:]) + copy(o.name[:], from.name[:]) + copy(o.comment[:], from.comment[:]) + o.opcode = from.opcode + o.flag = from.flag + + o.af = from.af + o.commentOrNomatch = from.commentOrNomatch + o.hashSize = from.hashSize + o.maxElem = from.maxElem + + o.proto = from.proto + o.cidr = from.cidr + o.addrRange.Copy(&from.addrRange) + o.iface = from.iface + o.macAddr = from.macAddr + + o.cidr2 = from.cidr2 + o.addrRange2.Copy(&from.addrRange2) + + return true +} + +func (o *IPSetParam) Dump(buf []byte) bool { + var to *IPSetParam + if len(buf) < int(o.Sizeof()) { + return false + } + to = *(**IPSetParam)(unsafe.Pointer(&buf)) + return o.Copy(to) +} + +func (o *IPSetParam) Package() []byte { + buf := new(bytes.Buffer) + binary.Write(buf, binary.LittleEndian, o) + return buf.Bytes() +} + +func (o *IPSetParam) write(conn *pool.Conn) error { + buf := o.Package() + n, err := conn.WriteN(buf, int(o.Sizeof())) + if err != nil { + return fmt.Errorf("IPSetParam write error: %v, %d of %d written\n", + err, n, o.Sizeof()) + } + return nil +} + +type IPSetParamArray []IPSetParam + +// IPSetMember mirrors `struct ipset_meber` defined in conf/ipset.h +type IPSetMember struct { + comment [IPSET_MAXCOMLEN]byte + + addr [16]byte + cidr uint8 + proto uint8 + port uint16 + iface [unix.IFNAMSIZ]byte + macAddr [6]byte + nomatch uint8 + + // for ipset types with 2 nets + cidr2 uint8 + port2 uint16 + _ [2]uint8 + addr2 [16]byte +} + +func (o *IPSetMember) GetComment() string { + return string(bytes.TrimRight(o.comment[:], "\x00")) +} + +func (o *IPSetMember) GetAddr(af uint8) net.IP { + if af == syscall.AF_INET6 { + res := make(net.IP, net.IPv6len) + copy(res, o.addr[:]) + return res + } + return net.IPv4(o.addr[0], o.addr[1], o.addr[2], o.addr[3]) +} + +func (o *IPSetMember) GetCidr() uint8 { + return o.cidr +} + +func (o *IPSetMember) GetProto() uint8 { + return o.proto +} + +func (o *IPSetMember) GetPort() uint16 { + return o.port +} + +func (o *IPSetMember) GetIface() string { + return string(bytes.TrimRight(o.iface[:], "\x00")) +} + +func (o *IPSetMember) GetMacAddr() string { + return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", + o.macAddr[0], o.macAddr[1], o.macAddr[2], + o.macAddr[3], o.macAddr[4], o.macAddr[5]) +} + +func (o *IPSetMember) GetNoMatch() bool { + if o.nomatch > 0 { + return true + } + return false +} + +func (o *IPSetMember) GetCidr2() uint8 { + return o.cidr2 +} + +func (o *IPSetMember) GetPort2() uint16 { + return o.port2 +} + +func (o *IPSetMember) GetAddr2(af uint8) net.IP { + if af == syscall.AF_INET6 { + res := make(net.IP, net.IPv6len) + copy(res, o.addr2[:]) + return res + } + return net.IPv4(o.addr2[0], o.addr2[1], o.addr2[2], o.addr2[3]) +} + +func (o *IPSetMember) Sizeof() uint64 { + return uint64(unsafe.Sizeof(*o)) +} + +func (o *IPSetMember) Copy(from *IPSetMember) bool { + if from == nil { + return false + } + + copy(o.comment[:], from.comment[:]) + + copy(o.addr[:], from.addr[:]) + o.cidr = from.cidr + o.proto = from.proto + o.port = from.port + copy(o.iface[:], from.iface[:]) + copy(o.macAddr[:], from.macAddr[:]) + o.nomatch = from.nomatch + + o.cidr2 = from.cidr2 + o.port2 = from.port2 + copy(o.addr2[:], from.addr2[:]) + + return true +} + +// IPSetInfo mirrors `struct ipset_info` defined in conf/ipset.h +type IPSetInfo struct { + name [IPSET_MAXNAMELEN]byte + kind [IPSET_MAXNAMELEN]byte + comment uint8 + + af uint8 + _ [2]uint8 + + // kind bitmap: cidr(8), addrRange(20) + // kind hash: hashSize(4), hashMaxElem(4) + cidr uint8 + _ [3]uint8 + hashSizeOrAddrRange uint32 + hashMaxElem uint32 + __reserved [28]uint8 + + size uint32 + entries uint32 + references uint32 + + membersPtr uintptr + members []IPSetMember +} + +func (o *IPSetInfo) GetName() string { + return string(bytes.TrimRight(o.name[:], "\x00")) +} + +func (o *IPSetInfo) GetKind() string { + return string(bytes.TrimRight(o.kind[:], "\x00")) +} + +func (o *IPSetInfo) GetComment() bool { + if o.comment > 0 { + return true + } + return false +} + +func (o *IPSetInfo) GetAf() uint8 { + return o.af +} + +func (o *IPSetInfo) GetCidr() uint8 { + return o.cidr +} + +func (o *IPSetInfo) GetAddrRange() (net.IP, net.IP, uint16, uint16) { + iaRange := (*InetAddrRange)(unsafe.Pointer(uintptr(unsafe.Pointer(&o.hashSizeOrAddrRange)))) + return iaRange.Decode(o.af) +} + +func (o *IPSetInfo) GetHashSize() uint32 { + return o.hashSizeOrAddrRange +} + +func (o *IPSetInfo) GetSize() uint32 { + return o.size +} + +func (o *IPSetInfo) GetEntries() uint32 { + return o.entries +} + +func (o *IPSetInfo) GetReferences() uint32 { + return o.references +} + +func (o *IPSetInfo) GetHashMaxElem() uint32 { + return o.hashMaxElem +} + +func (o *IPSetInfo) GetMembers() []IPSetMember { + return o.members +} + +func (o *IPSetInfo) Sizeof() uint64 { + return uint64(unsafe.Offsetof(o.members)) +} + +func (o *IPSetInfo) Copy(from *IPSetInfo) bool { + if from == nil { + return false + } + + copy(o.name[:], from.name[:]) + copy(o.kind[:], from.kind[:]) + o.comment = from.comment + + o.af = from.af + + o.cidr = from.cidr + o.hashSizeOrAddrRange = from.hashSizeOrAddrRange + o.hashMaxElem = from.hashMaxElem + copy(o.__reserved[:], from.__reserved[:]) + + o.size = from.size + o.entries = from.entries + o.references = from.references + + //// Note: + //// Do NOT copy members! They are not in C struct. + // o.members = make([]IPSetMember, len(from.members)) + // for i, _ := range from.members { + // o.members[i].Copy(&from.members[i]) + // } + + return true +} + +// IPSetInfoArray interprets `struct ipset_info_array` defined in conf/ipset.h +type IPSetInfoArray struct { + infos []IPSetInfo +} + +func (o *IPSetInfoArray) GetIPSetInfos() []IPSetInfo { + return o.infos +} + +func (o *IPSetInfoArray) read(conn *pool.Conn, logger hclog.Logger) error { + var info *IPSetInfo + var member *IPSetMember + var i, j, nipset uint32 + var offset uint64 + + dataLen := uint64(unsafe.Sizeof(nipset)) + buf, err := conn.ReadN(int(dataLen)) + if err != nil { + return fmt.Errorf("Read IPSetInfo number failed: %v", err) + } + nipset = binary.LittleEndian.Uint32(buf[:dataLen]) + if nipset == 0 { + return nil + } + + // read IPSetInfo data + dataLen = (uint64(nipset)) * info.Sizeof() + buf, err = conn.ReadN(int(dataLen)) + if err != nil { + return fmt.Errorf("Read IPSetInfo data failed: %v", err) + } + + dataLen = 0 + offset = 0 + o.infos = make([]IPSetInfo, nipset) + for i = 0; i < nipset; i++ { + info = (*IPSetInfo)(unsafe.Pointer(uintptr(unsafe.Pointer(&buf[offset])))) + o.infos[i].Copy(info) + offset += info.Sizeof() + dataLen += uint64(info.entries) * member.Sizeof() + } + if dataLen == 0 { + return nil + } + + // read IPSetMember data + buf, err = conn.ReadN(int(dataLen)) + if err != nil { + return fmt.Errorf("Read IPSetMember data failed: %v", err) + } + offset = 0 + for i = 0; i < nipset; i++ { + o.infos[i].members = make([]IPSetMember, o.infos[i].entries) + for j = 0; j < o.infos[i].entries; j++ { + member = (*IPSetMember)(unsafe.Pointer(uintptr(unsafe.Pointer(&buf[offset])))) + o.infos[i].members[j].Copy(member) + offset += member.Sizeof() + } + } + + return nil +} + +type CheckResult int32 + +func (o *CheckResult) Sizeof() uint64 { + return uint64(unsafe.Sizeof(*o)) +} + +func (o *CheckResult) Dump(buf []byte) bool { + if len(buf) != int(o.Sizeof()) { + return false + } + reader := bytes.NewReader(buf) + if err := binary.Read(reader, binary.LittleEndian, o); err != nil { + return false + } + return true +} + +func (o *CheckResult) read(conn *pool.Conn, logger hclog.Logger) error { + buf, err := conn.ReadN(int(o.Sizeof())) + if err != nil { + return fmt.Errorf("Read ipset check result failed: %v", err) + } + if o.Dump(buf) != true { + return fmt.Errorf("Dump ipset check result failed") + } + return nil +} + +func getLogger(name string, parent hclog.Logger) hclog.Logger { + if parent != nil { + return parent.Named(name) + } + return hclog.Default().Named(name) +} + +func (o *IPSetParam) Get(cp *pool.ConnPool, parentLogger hclog.Logger) (*IPSetInfoArray, error, DpvsErrType) { + logger := getLogger("ipset:get", parentLogger) + + if o.opcode != IPSET_OP_LIST { + logger.Error("Invalid ipset opcode for Get", "opcode", o.opcode) + return nil, fmt.Errorf("invalid ipset opcode %d for get", o.opcode), 0 + } + + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return nil, err, 0 + } + defer cp.Remove(ctx, conn, nil) + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_GET_IPSET_LIST, SOCKOPT_GET, o.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_LIST write proto header failed", "Error", err.Error()) + return nil, err, 0 + } + + if err = o.write(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_LIST write ipset param failed", "Error", err.Error()) + return nil, err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_LIST read reply header failed", "Error", err.Error()) + return nil, err, 0 + } + if reply.GetErrCode() != EDPVS_OK { + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_GET_IPSET_LIST replied error", "DPVS.Error", errStr) + return nil, fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + + output := &IPSetInfoArray{} + if reply.GetLen() > 0 { + err = output.read(conn, logger) + if err != nil { + logger.Error("SOCKOPT_GET_IPSET_LIST read reply data failed", "Error", err.Error()) + return nil, err, 0 + } + } + return output, nil, 0 +} + +func (o *IPSetParam) CreateDestroy(cp *pool.ConnPool, parentLogger hclog.Logger) (error, DpvsErrType) { + if o.opcode != IPSET_OP_CREATE && o.opcode != IPSET_OP_DESTROY { + return fmt.Errorf("invalid ipset opcode %d for Create/Destroy", o.opcode), 0 + } + logName := "ipset:create" + if o.opcode == IPSET_OP_DESTROY { + logName = "ipset:destroy" + } + logger := getLogger(logName, parentLogger) + + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return err, 0 + } + defer cp.Remove(ctx, conn, nil) + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_SET_IPSET, SOCKOPT_SET, o.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write proto header failed", "Error", err.Error()) + return err, 0 + } + + if err = o.write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write ipset param failed", "Error", err.Error()) + return err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET read reply header failed", "Error", err.Error()) + return err, 0 + } + dpvsErrCode := reply.GetErrCode() + if dpvsErrCode != EDPVS_OK { + /* + if !(dpvsErrCode == EDPVS_EXIST && o.opcode == IPSET_OP_CREATE || + dpvsErrCode == EDPVS_NOTEXIST && o.opcode == IPSET_OP_DESTROY) { + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_SET_IPSET replied error", "DPVS.Error", errStr) + return fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + */ + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_SET_IPSET replied error", "DPVS.Error", errStr) + return fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + return nil, 0 +} + +func (o *IPSetParam) IsIn(cp *pool.ConnPool, parentLogger hclog.Logger) (bool, error, DpvsErrType) { + logger := getLogger("ipset:isin", parentLogger) + + result := false + if o.opcode != IPSET_OP_TEST { + logger.Error("Invalid ipset opcode for TEST", "opcode", o.opcode) + return result, fmt.Errorf("invalid ipset opcode %d for TEST", o.opcode), 0 + } + + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return result, err, 0 + } + defer cp.Remove(ctx, conn, nil) + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_GET_IPSET_TEST, SOCKOPT_GET, o.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_TEST write proto header failed", "Error", err.Error()) + return result, err, 0 + } + + if err = o.write(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_TEST write ipset param failed", "Error", err.Error()) + return result, err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_GET_IPSET_TEST read reply header failed", "Error", err.Error()) + return result, err, 0 + } + if reply.GetErrCode() != EDPVS_OK { + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_GET_IPSET_TEST replied error", "DPVS.Error", errStr) + return result, fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + + var output CheckResult + err = output.read(conn, logger) + if err != nil { + logger.Error("SOCKOPT_GET_IPSET_LIST read reply data failed", "Error", err.Error()) + return result, err, 0 + } + if output > 0 { + result = true + } + return result, nil, 0 +} + +func (o *IPSetParamArray) AddDelMember(cp *pool.ConnPool, parentLogger hclog.Logger) (error, DpvsErrType) { + if len(*o) == 0 { + return nil, 0 + } + opcode := (*o)[0].opcode + if opcode != IPSET_OP_ADD && opcode != IPSET_OP_DEL { + return fmt.Errorf("invalid ipset opcode %d for Add/Del", opcode), 0 + } + name := (*o)[0].name + for _, param := range *o { + if opcode != param.opcode { + return fmt.Errorf("ipset opcode in param array did not match for Add/Del"), 0 + } + if !bytes.Equal(name[:], param.name[:]) { + return fmt.Errorf("ipset name in param array did not match for Add/Del"), 0 + } + } + + logName := "ipset:add" + if opcode == IPSET_OP_DEL { + logName = "ipset:del" + } + logger := getLogger(logName, parentLogger) + + for _, param := range *o { + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return err, 0 + } + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_SET_IPSET, SOCKOPT_SET, param.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write proto header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + if err = param.write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write ipset param failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET read reply header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + cp.Remove(ctx, conn, nil) + + dpvsErrCode := reply.GetErrCode() + if dpvsErrCode != EDPVS_OK { + /* + if dpvsErrCode == EDPVS_EXIST && opcode == IPSET_OP_ADD || + dpvsErrCode == EDPVS_NOTEXIST && opcode == IPSET_OP_DEL { + continue + } + */ + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_SET_IPSET replied error", "DPVS.Error", errStr) + return fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + } + return nil, 0 +} + +func (o *IPSetParamArray) ReplaceMember(cp *pool.ConnPool, parentLogger hclog.Logger) (error, DpvsErrType) { + if len(*o) == 0 { + return nil, 0 + } + opcode := (*o)[0].opcode + if opcode != IPSET_OP_ADD && opcode != IPSET_OP_FLUSH { + return fmt.Errorf("invalid ipset opcode %d for Replace", opcode), 0 + } + name := (*o)[0].name + for i, param := range *o { + if i == 0 { + continue + } + if opcode != param.opcode { + return fmt.Errorf("ipset opcode in param array did not match for Replace"), 0 + } + if !bytes.Equal(name[:], param.name[:]) { + return fmt.Errorf("ipset name in param array did not match for Repalce"), 0 + } + } + + logger := getLogger("replace", parentLogger) + + // Flush the whole ipset + param := &IPSetParam{} + param.Copy(&(*o)[0]) + param.opcode = IPSET_OP_FLUSH + + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return err, 0 + } + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_SET_IPSET, SOCKOPT_SET, param.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write proto header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + if err = param.write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write ipset param failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET read reply header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + cp.Remove(ctx, conn, nil) + + if reply.GetErrCode() != EDPVS_OK { + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_SET_IPSET replied error", "DPVS.Error", errStr) + return fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + + if opcode != IPSET_OP_ADD { + return nil, 0 + } + + // Add members into ipset + for _, param := range *o { + ctx := context.Background() + conn, err := cp.Get(ctx) + if err != nil { + logger.Error("Get conn from pool failed", "Error", err.Error()) + return err, 0 + } + + msg := NewSockMsg(SOCKOPT_VERSION, SOCKOPT_SET_IPSET, SOCKOPT_SET, param.Sizeof()) + if err = msg.Write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write proto header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + if err = param.write(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET write ipset param failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + + reply := NewReplySockMsg() + if err = reply.Read(conn); err != nil { + logger.Error("SOCKOPT_SET_IPSET read reply header failed", "Error", err.Error()) + cp.Remove(ctx, conn, nil) + return err, 0 + } + cp.Remove(ctx, conn, nil) + + dpvsErrCode := reply.GetErrCode() + if dpvsErrCode != EDPVS_OK { + errStr := reply.GetErrStr() + logger.Error("SOCKOPT_SET_IPSET replied error", "DPVS.Error", errStr) + return fmt.Errorf("DPVS Response Error: %s", errStr), reply.GetErrCode() + } + } + return nil, 0 +} diff --git a/tools/dpvs-agent/pkg/ipc/types/ipset_models.go b/tools/dpvs-agent/pkg/ipc/types/ipset_models.go new file mode 100644 index 000000000..203cad130 --- /dev/null +++ b/tools/dpvs-agent/pkg/ipc/types/ipset_models.go @@ -0,0 +1,1305 @@ +// Copyright 2023 IQiYi Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/binary" + "fmt" + "net" + "strconv" + "strings" + "syscall" + "unicode" + + "github.com/dpvs-agent/models" +) + +var ( + _ IPSetType = (*IPSetBitmapIP)(nil) + _ IPSetType = (*IPSetBitmapIPMac)(nil) + _ IPSetType = (*IPSetBitmapPort)(nil) + _ IPSetType = (*IPSetHashIP)(nil) + _ IPSetType = (*IPSetHashNet)(nil) + _ IPSetType = (*IPSetHashIPPort)(nil) + _ IPSetType = (*IPSetHashNetPort)(nil) + _ IPSetType = (*IPSetHashNetPortIface)(nil) + _ IPSetType = (*IPSetHashIPPortIP)(nil) + _ IPSetType = (*IPSetHashIPPortNet)(nil) + _ IPSetType = (*IPSetHashNetPortNet)(nil) + _ IPSetType = (*IPSetHashNetPortNetPort)(nil) +) + +type IPSetType interface { + // Update IPSetParam with parsed fields from models.IpsetMember.Entry + ParseEntry(string, *IPSetParam) error + // Create a models.IpsetMember with Entry field filled + ModelEntry(uint8, *IPSetMember) (*models.IpsetMember, error) + // Check if IPSetParam is valid + CheckParam(*IPSetParam) error +} + +type IPSetBitmapIP struct{} +type IPSetBitmapIPMac struct{} +type IPSetBitmapPort struct{} +type IPSetHashIP struct{} +type IPSetHashNet struct{} +type IPSetHashIPPort struct{} +type IPSetHashNetPort struct{} +type IPSetHashNetPortIface struct{} +type IPSetHashIPPortIP struct{} +type IPSetHashIPPortNet struct{} +type IPSetHashNetPortNet struct{} +type IPSetHashNetPortNetPort struct{} + +func (o *IPSetBitmapIP) ParseEntry(entry string, param *IPSetParam) error { + startIP, endIP, af, pfx, err := parseAddrRange(entry) + if err != nil { + return fmt.Errorf("Parse models.IpsetMember Entry failed: %v", err) + } + + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + return nil +} + +func (o *IPSetBitmapIP) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + entry := "" + + if member.GetCidr() > 0 { + entry = fmt.Sprintf("%s/%d", member.GetAddr(af), member.GetCidr()) + } else { + entry = fmt.Sprintf("%s", member.GetAddr(af)) + } + model.Entry = &entry + + return model, nil +} + +func (o *IPSetBitmapIP) CheckParam(param *IPSetParam) error { + if param.af == syscall.AF_INET6 { + return fmt.Errorf("bitmap:ip doesn't support ipv6") + } + if param.opcode != IPSET_OP_CREATE { + return nil + } + if param.cidr > 0 { + if param.cidr < 16 { + return fmt.Errorf("bitmap:ip net seg too big, cidr should be no smaller than 16") + } + return nil + } + if param.af == syscall.AF_INET { + startIP, endIP, _, _ := param.addrRange.Decode(param.af) + if ip4ToUint32(startIP) >= ip4ToUint32(endIP) { + return fmt.Errorf("bitmap:ip requires a network range or cidr") + } + } + return nil +} + +func (o *IPSetBitmapIPMac) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s", segs[0]) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + if len(segs) > 1 { + if err := param.SetMacAddr(segs[1]); err != nil { + return fmt.Errorf("invalid mac address: %s", err.Error()) + } + } + + return nil +} + +func (o *IPSetBitmapIPMac) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + entry := fmt.Sprintf("%s,%s", member.GetAddr(af), member.GetMacAddr()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetBitmapIPMac) CheckParam(param *IPSetParam) error { + if param.af == syscall.AF_INET6 { + return fmt.Errorf("bitmap:ip,mac doesn't support ipv6") + } + if param.opcode != IPSET_OP_CREATE { + if param.cidr > 0 { + return fmt.Errorf("bitmap:ip,mac doesn't support addr cidr") + } + if param.af == syscall.AF_INET { + startIP, endIP, _, _ := param.addrRange.Decode(param.af) + if endIP != nil && !endIP.Equal(startIP) { + return fmt.Errorf("bitmap:ip,mac doesn't support addr range") + } + } + } else { + if param.cidr > 0 { + if param.cidr < 16 { + return fmt.Errorf("bitmap:ip,mac net seg too big, cidr should be no smaller than 16") + } + return nil + } + if param.af == syscall.AF_INET { + startIP, endIP, _, _ := param.addrRange.Decode(param.af) + if ip4ToUint32(startIP) >= ip4ToUint32(endIP) { + return fmt.Errorf("bitmap:ip,mac create requires a network range or cidr") + } + } + } + return nil +} + +func (o *IPSetBitmapPort) ParseEntry(entry string, param *IPSetParam) error { + startPort, endPort, proto, err := parsePortRange(entry) + if err != nil { + return err + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + return nil +} + +func (o *IPSetBitmapPort) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s:%d", protoString(member.GetProto()), member.GetPort()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetBitmapPort) CheckParam(param *IPSetParam) error { + if param.opcode == IPSET_OP_CREATE { + if param.proto != 0 { + return fmt.Errorf("bitmap:port doesn't support proto in create") + } + } else { + if param.addrRange.minPort > 0 && + param.proto != syscall.IPPROTO_TCP && param.proto != syscall.IPPROTO_UDP { + return fmt.Errorf("invalid bitmap:port protocol %s", protoString(param.proto)) + } + } + return nil +} + +func (o *IPSetHashIP) ParseEntry(entry string, param *IPSetParam) error { + startIP, endIP, af, pfx, err := parseAddrRange(entry) + if err != nil { + return fmt.Errorf("invalid addr range %s", entry) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + return nil +} + +func (o *IPSetHashIP) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := member.GetAddr(af).String() + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashIP) CheckParam(param *IPSetParam) error { + if param.opcode != IPSET_OP_ADD && param.opcode != IPSET_OP_DEL { + return nil + } + if param.af == syscall.AF_INET6 { + if param.cidr > 0 { + return fmt.Errorf("hash:ip doesn't support IPv6 cidr") + } + } else if param.af == syscall.AF_INET { + if param.cidr > 0 && param.cidr < 16 { + return fmt.Errorf("ipv4 address cidr range too big, 65536 at most") + } + } + startIP, endIP, _, _ := param.addrRange.Decode(param.af) + if param.af == syscall.AF_INET { + startIPNum, endIPNum := ip4ToUint32(startIP), ip4ToUint32(endIP) + if endIPNum > 0 && endIPNum-startIPNum >= 65535 { + return fmt.Errorf("ipv4 address range too big, 65536 at most") + } + } + return nil +} + +func (o *IPSetHashNet) ParseEntry(entry string, param *IPSetParam) error { + // the same as IPSetHashIP + var iphash IPSetHashIP + return iphash.ParseEntry(entry, param) +} + +func (o *IPSetHashNet) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s/%d", member.GetAddr(af).String(), member.GetCidr()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashNet) CheckParam(param *IPSetParam) error { + // nothing to do + return nil +} + +func (o *IPSetHashIPPort) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s", segs[0]) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + if len(segs) > 1 { + startPort, endPort, proto, err := parsePortRange(segs[1]) + if err != nil { + return err + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + } + + return nil +} + +func (o *IPSetHashIPPort) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s,%s:%d", + member.GetAddr(af).String(), + protoString(member.GetProto()), + member.GetPort()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashIPPort) CheckParam(param *IPSetParam) error { + if param.opcode != IPSET_OP_ADD && param.opcode != IPSET_OP_DEL { + return nil + } + if param.af == syscall.AF_INET6 { + if param.cidr > 0 { + return fmt.Errorf("hash:ip,port doesn't support IPv6 cidr") + } + } else if param.af == syscall.AF_INET { + if param.cidr > 0 && param.cidr < 24 { + return fmt.Errorf("ipv4 address cidr range too big, 256 at most") + } + } + + startIP, endIP, startPort, endPort := param.addrRange.Decode(param.af) + if param.af == syscall.AF_INET { + startIPNum, endIPNum := ip4ToUint32(startIP), ip4ToUint32(endIP) + if endIPNum > 0 && endIPNum-startIPNum >= 256 { + return fmt.Errorf("ipv4 address range too big, 256 at most") + } + } + if endPort > 0 && endPort-startPort >= 256 { + return fmt.Errorf("port range too big, 256 at most") + } + return nil +} + +func (o *IPSetHashNetPort) ParseEntry(entry string, param *IPSetParam) error { + // the same as IPSetHashIPPort + var ipport IPSetHashIPPort + return ipport.ParseEntry(entry, param) +} + +func (o *IPSetHashNetPort) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s/%d,%s:%d", + member.GetAddr(af).String(), + member.GetPort(), + protoString(member.GetProto()), + member.GetPort()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashNetPort) CheckParam(param *IPSetParam) error { + // nothing to do + return nil +} + +func (o *IPSetHashNetPortIface) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + if len(segs) < 3 { + return fmt.Errorf("invalid hash:net,port,iface entry: %s", entry) + } + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + segs = segs[1:] + startPort, endPort, proto, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range %s, error %v", segs[0], err) + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + + segs = segs[1:] + if len(segs[0]) == 0 { + return fmt.Errorf("empty interface name") + } + param.SetIface(segs[0]) + + return nil +} + +func (o *IPSetHashNetPortIface) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s/%d,%s:%d,%s", + member.GetAddr(af).String(), + member.GetPort(), + protoString(member.GetProto()), + member.GetPort(), + member.GetIface()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashNetPortIface) CheckParam(param *IPSetParam) error { + // nothing to do + return nil +} + +func (o *IPSetHashIPPortIP) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + if len(segs) < 3 { + return fmt.Errorf("invalid hash:ip,port,ip entry: %s", entry) + } + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + segs = segs[1:] + startPort, endPort, proto, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range %s, err %v", segs[0], err) + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + + segs = segs[1:] + startIP2, endIP2, af2, pfx2, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range2 %s, error %v", segs[0], err) + } + if af2 != af { + return fmt.Errorf("address family mismatch in hash:ip,port,ip member") + } + if startIP2 != nil { + param.GetAddrRange2().SetMinAddr(startIP2) + } + if endIP2 != nil { + param.GetAddrRange2().SetMaxAddr(endIP2) + } else { + param.GetAddrRange2().SetMaxAddr(startIP2) + } + param.SetCidr2(pfx2) + + return nil +} + +func (o *IPSetHashIPPortIP) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s,%s:%d,%s", + member.GetAddr(af).String(), + protoString(member.GetProto()), + member.GetPort(), + member.GetAddr2(af).String()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashIPPortIP) CheckParam(param *IPSetParam) error { + if param.opcode != IPSET_OP_ADD && param.opcode != IPSET_OP_DEL { + return nil + } + + if param.af == syscall.AF_INET6 { + if param.cidr > 0 || param.cidr2 > 0 { + return fmt.Errorf("hash:ip,port,ip doesn't support IPv6 cidr") + } + } else if param.af == syscall.AF_INET { + if param.cidr > 0 && param.cidr < 24 { + return fmt.Errorf("ipv4 address cidr range too big, 256 at most") + } + if param.cidr2 > 0 && param.cidr2 < 24 { + return fmt.Errorf("ipv4 address cidr2 range too big, 256 at most") + } + } + + startIP, endIP, startPort, endPort := param.addrRange.Decode(param.af) + if param.af == syscall.AF_INET { + startIPNum, endIPNum := ip4ToUint32(startIP), ip4ToUint32(endIP) + if endIPNum > 0 && endIPNum-startIPNum >= 256 { + return fmt.Errorf("ipv4 address range too big, 256 at most") + } + } + if endPort > 0 && endPort-startPort >= 256 { + return fmt.Errorf("port range too big, 256 at most") + } + + startIP2, endIP2, _, _ := param.addrRange.Decode(param.af) + if param.af == syscall.AF_INET { + startIPNum2, endIPNum2 := ip4ToUint32(startIP2), ip4ToUint32(endIP2) + if endIPNum2 > 0 && endIPNum2-startIPNum2 >= 256 { + return fmt.Errorf("ipv4 address range2 too big, 256 at most") + } + } + + return nil +} + +func (o *IPSetHashIPPortNet) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + if len(segs) < 3 { + return fmt.Errorf("invalid hash:ip,port,net entry: %s", entry) + } + + // Notes: The "ip" and "net" parts in hash:ip,port,net corresponds to addr range1 + // and addr range2 respectively, so that match from a source address network + // to a single dest address can be implemented easily. + + // the "ip" part corresponds to address range2 + startIP2, endIP2, af2, pfx2, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + param.SetAf(af2) + if startIP2 != nil { + param.GetAddrRange2().SetMinAddr(startIP2) + } + if endIP2 != nil { + param.GetAddrRange2().SetMaxAddr(endIP2) + } else { + param.GetAddrRange2().SetMaxAddr(startIP2) + } + param.SetCidr2(pfx2) + + // the "port" part + segs = segs[1:] + startPort, endPort, proto, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range %s, err %v", segs[0], err) + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + + // the "net" part corresponds to address range1 + segs = segs[1:] + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + if af != af2 { + return fmt.Errorf("address family mismatch in hash:ip,port,net member") + } + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + return nil +} + +func (o *IPSetHashIPPortNet) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s/%d,%s:%d,%s", + member.GetAddr(af).String(), + member.GetCidr(), + protoString(member.GetProto()), + member.GetPort(), + member.GetAddr2(af).String()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashIPPortNet) CheckParam(param *IPSetParam) error { + if param.opcode != IPSET_OP_ADD && param.opcode != IPSET_OP_DEL { + return nil + } + + if param.af == syscall.AF_INET6 { + if param.cidr2 > 0 { + return fmt.Errorf("hash:ip,port,net doesn't support IPv6 cidr") + } + } else if param.af == syscall.AF_INET { + if param.cidr2 > 0 && param.cidr2 < 24 { + return fmt.Errorf("ipv4 address cidr2 range too big, 256 at most") + } + } + + _, _, startPort, endPort := param.addrRange.Decode(param.af) + if endPort > 0 && endPort-startPort >= 256 { + return fmt.Errorf("port range too big, 256 at most") + } + + startIP2, endIP2, _, _ := param.addrRange.Decode(param.af) + if param.af == syscall.AF_INET { + startIPNum2, endIPNum2 := ip4ToUint32(startIP2), ip4ToUint32(endIP2) + if endIPNum2 > 0 && endIPNum2-startIPNum2 >= 256 { + return fmt.Errorf("ipv4 address range2 too big, 256 at most") + } + } + + return nil +} + +func (o *IPSetHashNetPortNet) ParseEntry(entry string, param *IPSetParam) error { + // Notes: almost the same as IPSetHashIPPortIP except the error message, + + segs := strings.Split(entry, ",") + if len(segs) < 3 { + return fmt.Errorf("invalid hash:net,port,net entry: %s", entry) + } + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + segs = segs[1:] + startPort, endPort, proto, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range %s, err %v", segs[0], err) + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + + segs = segs[1:] + startIP2, endIP2, af2, pfx2, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range2 %s, error %v", segs[0], err) + } + if af2 != af { + return fmt.Errorf("address family mismatch in hash:net,port,net member") + } + if startIP2 != nil { + param.GetAddrRange2().SetMinAddr(startIP2) + } + if endIP2 != nil { + param.GetAddrRange2().SetMaxAddr(endIP2) + } else { + param.GetAddrRange2().SetMaxAddr(startIP2) + } + param.SetCidr2(pfx2) + + return nil +} + +func (o *IPSetHashNetPortNet) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + entry := fmt.Sprintf("%s/%d,%s:%d,%s/%d", + member.GetAddr(af).String(), + member.GetCidr(), + protoString(member.GetProto()), + member.GetPort(), + member.GetAddr2(af).String(), + member.GetCidr2()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashNetPortNet) CheckParam(param *IPSetParam) error { + // nothing to do + return nil +} + +func (o *IPSetHashNetPortNetPort) ParseEntry(entry string, param *IPSetParam) error { + segs := strings.Split(entry, ",") + if len(segs) < 4 { + return fmt.Errorf("invalid hash:net,port,net,port entry: %s", entry) + } + + startIP, endIP, af, pfx, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range %s, error %v", segs[0], err) + } + param.SetAf(af) + if startIP != nil { + param.GetAddrRange().SetMinAddr(startIP) + } + if endIP != nil { + param.GetAddrRange().SetMaxAddr(endIP) + } else { + param.GetAddrRange().SetMaxAddr(startIP) + } + param.SetCidr(pfx) + + segs = segs[1:] + startPort, endPort, proto, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range %s, err %v", segs[0], err) + } + param.GetAddrRange().SetMinPort(startPort) + if endPort > 0 { + param.GetAddrRange().SetMaxPort(endPort) + } else { + param.GetAddrRange().SetMaxPort(startPort) + } + param.SetProto(proto) + + segs = segs[1:] + startIP2, endIP2, af2, pfx2, err := parseAddrRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid addr range2 %s, error %v", segs[0], err) + } + if af2 != af { + return fmt.Errorf("address family mismatch in hash:net,port,net,port member") + } + if startIP2 != nil { + param.GetAddrRange2().SetMinAddr(startIP2) + } + if endIP2 != nil { + param.GetAddrRange2().SetMaxAddr(endIP2) + } else { + param.GetAddrRange2().SetMaxAddr(startIP2) + } + param.SetCidr2(pfx2) + + segs = segs[1:] + startPort2, endPort2, proto2, err := parsePortRange(segs[0]) + if err != nil { + return fmt.Errorf("invalid port range2 %s, err %v", segs[0], err) + } + if proto2 != proto { + return fmt.Errorf("protocol mismatch in hash:net,port,net,port member") + } + param.GetAddrRange2().SetMinPort(startPort2) + if endPort2 > 0 { + param.GetAddrRange2().SetMaxPort(endPort2) + } else { + param.GetAddrRange2().SetMaxPort(startPort2) + } + + return nil +} + +func (o *IPSetHashNetPortNetPort) ModelEntry(af uint8, member *IPSetMember) (*models.IpsetMember, error) { + model := &models.IpsetMember{} + + proto := protoString(member.GetProto()) + entry := fmt.Sprintf("%s/%d,%s:%d,%s/%d,%s:%d", + member.GetAddr(af).String(), + member.GetCidr(), + proto, member.GetPort(), + member.GetAddr2(af).String(), + member.GetCidr2(), + proto, member.GetPort2()) + model.Entry = &entry + + return model, nil +} + +func (o *IPSetHashNetPortNetPort) CheckParam(param *IPSetParam) error { + // nothing to do + return nil +} + +var ipsetTypes = map[models.IpsetType]IPSetType{ + models.IpsetTypeBitmapIP: &IPSetBitmapIP{}, + models.IpsetTypeBitmapIPMac: &IPSetBitmapIPMac{}, + models.IpsetTypeBitmapPort: &IPSetBitmapPort{}, + models.IpsetTypeHashIP: &IPSetHashIP{}, + models.IpsetTypeHashNet: &IPSetHashNet{}, + models.IpsetTypeHashIPPort: &IPSetHashIPPort{}, + models.IpsetTypeHashNetPort: &IPSetHashNetPort{}, + models.IpsetTypeHashNetPortIface: &IPSetHashNetPortIface{}, + models.IpsetTypeHashIPPortIP: &IPSetHashIPPortIP{}, + models.IpsetTypeHashIPPortNet: &IPSetHashIPPortNet{}, + models.IpsetTypeHashNetPortNet: &IPSetHashNetPortNet{}, + models.IpsetTypeHashNetPortNetPort: &IPSetHashNetPortNetPort{}, +} + +func IPSetTypeGet(kind models.IpsetType) IPSetType { + return ipsetTypes[kind] +} + +func afCode(family string) uint8 { + switch strings.ToLower(family) { + case "": + return syscall.AF_UNSPEC + case "ipv4": + return syscall.AF_INET + case "ipv6": + return syscall.AF_INET6 + default: + return syscall.AF_MAX + } +} + +func afString(af uint8) string { + switch af { + case syscall.AF_INET: + return "ipv4" + case syscall.AF_INET6: + return "ipv6" + default: + return "not-supported" + } +} + +func protoString(proto uint8) string { + switch proto { + case syscall.IPPROTO_TCP: + return "tcp" + case syscall.IPPROTO_UDP: + return "udp" + case syscall.IPPROTO_ICMP: + return "icmp" + case syscall.IPPROTO_ICMPV6: + return "icmp6" + } + return "unspec" +} + +func ip4ToUint32(ip net.IP) uint32 { + ip4 := ip.To4() + if ip4 == nil { + return 0 + } + return binary.BigEndian.Uint32(ip4) +} + +// Parse IP range +// Format: +// +// { IPv4 | IPv4-IPv4 | IPv4/pfx4 | IPv6 | IPv6/pfx6 } +// +// Example: +// - 192.168.1.0/24 +// - 192.168.88.100-120 +// - 2001::/112 +func parseAddrRange(ar string) (startIP, endIP net.IP, af, cidr uint8, err error) { + if strings.Contains(ar, ":") { + af = syscall.AF_INET6 + } else { + af = syscall.AF_INET + } + + if af == syscall.AF_INET { + if strings.Contains(ar, "-") { + parts := strings.Split(ar, "-") + if len(parts) != 2 { + err = fmt.Errorf("invalid IPv4 range format %q", ar) + return + } + startIP = net.ParseIP(parts[0]).To4() + endIP = net.ParseIP(parts[1]).To4() + if startIP == nil || endIP == nil { + err = fmt.Errorf("invalid IPv4 address %q", ar) + return + } + if ip4ToUint32(startIP) > ip4ToUint32(endIP) { + err = fmt.Errorf("invalid IPv4 range %q", ar) + return + } + } else if strings.Contains(ar, "/") { + ip, ipNet, err2 := net.ParseCIDR(ar) + if err2 != nil { + err = fmt.Errorf("invalid IPv4 CIDR format: %v", err2) + return + } + startIP = ip.To4() + pfx, _ := ipNet.Mask.Size() + cidr = uint8(pfx) + } else { + if startIP = net.ParseIP(ar); startIP != nil { + startIP = startIP.To4() + } + if startIP == nil { + err = fmt.Errorf("unsupported IPv4 format") + return + } + } + } else { // syscall.AF_INET6 + if strings.Contains(ar, "/") { + ip, ipNet, err2 := net.ParseCIDR(ar) + if err2 != nil { + err = fmt.Errorf("invalid IPv6 CIDR format: %v", err2) + return + } + startIP = ip.To16() + pfx, _ := ipNet.Mask.Size() + cidr = uint8(pfx) + } else { + startIP = net.ParseIP(ar) + if startIP == nil { + err = fmt.Errorf("unsupported IPv6 format") + return + } + } + } + + return +} + +// Format: +// +// PROTO:PORT[-PORT] +// PROTO := tcp | udp | icmp | icmp6 +// PORT := NUM(0-65535) +// +// Example: +// +// tcp:8080-8082 +func parsePortRange(pr string) (port1, port2 uint16, proto uint8, err error) { + parts := strings.Split(pr, ":") + if len(parts) > 2 { + err = fmt.Errorf("too many segments in %q", pr) + return + } + + if len(parts) > 1 { + protoStr := strings.ToLower(parts[0]) + switch protoStr { + case "tcp": + proto = syscall.IPPROTO_TCP + case "udp": + proto = syscall.IPPROTO_UDP + case "icmp": + proto = syscall.IPPROTO_ICMP + case "icmp6": + proto = syscall.IPPROTO_ICMPV6 + default: + err = fmt.Errorf("invalid protocol %q", protoStr) + return + } + parts = parts[1:] + } + + portRange := parts[0] + parts = strings.Split(portRange, "-") + if len(parts) > 2 { + err = fmt.Errorf("too many segments in port range %q", portRange) + return + } + + _port1, err := strconv.ParseUint(parts[0], 10, 16) + if err != nil { + err = fmt.Errorf("invalid port number %q", parts[0]) + return + } + port1 = uint16(_port1) + + if len(parts) > 1 { + var _port2 uint64 + _port2, err = strconv.ParseUint(parts[1], 10, 16) + if err != nil { + err = fmt.Errorf("invalid port number %q", parts[1]) + return + } + port2 = uint16(_port2) + } + return +} + +func (o *IPSetParam) Build(model *models.IpsetInfo) error { + if len(model.Entries) > 0 && model.CreationOptions != nil { + return fmt.Errorf("Entries and CreationOptions cannot both set") + } + if len(model.Entries) > 1 { + return fmt.Errorf("More than 1 entry set for IPSetParam") + } + + o.SetName(*model.Name) + o.SetKind(string(*model.Type)) + + if model.CreationOptions != nil { + options := model.CreationOptions + if options.Comment != nil { + o.SetCommentFlag(*options.Comment) + } + o.SetMaxElem(options.HashMaxElem) + o.SetHashSize(options.HashSize) + af := afCode(options.Family) + if af == syscall.AF_MAX { + return fmt.Errorf("Unsupported address family %q", options.Family) + } + o.SetAf(af) + if len(options.Range) > 0 { + if strings.ContainsAny(options.Range, ".:") && unicode.Is(unicode.ASCII_Hex_Digit, + rune(options.Range[0])) { // IPv4 or IPv6 + startIP, endIP, af2, pfx, err := parseAddrRange(options.Range) + if err != nil { + return err + } + if af2 != af { + if af == syscall.AF_UNSPEC { + o.SetAf(af2) + } else { + return fmt.Errorf("Address family mismatch") + } + } + o.GetAddrRange().SetMinAddr(startIP) + if endIP != nil { + o.GetAddrRange().SetMaxAddr(endIP) + } + if pfx > 0 { + o.SetCidr(pfx) + } + } else { // Port + startPort, endPort, proto, err := parsePortRange(options.Range) + if err != nil { + return err + } + o.GetAddrRange().SetMinPort(startPort) + if endPort != 0 { + o.GetAddrRange().SetMaxPort(endPort) + } else { + o.GetAddrRange().SetMaxPort(startPort) + } + o.SetProto(proto) + } + } + } + + if len(model.Entries) > 0 { + return o.BuildMember(model.Entries[0]) + } + return nil +} + +func (o *IPSetParamArray) Build(opcode uint16, model *models.IpsetInfo) error { + if opcode == IPSET_OP_FLUSH { + param := new(IPSetParam) + param.SetOpcode(opcode) + param.SetName(*model.Name) + param.SetKind(string(*model.Type)) + *o = append(*o, *param) + return nil + } + + if len(model.Entries) < 1 { + return fmt.Errorf("No Entries found in IpsetInfo model") + } + if model.CreationOptions != nil { + return fmt.Errorf("CreationOptions supplied with multiple Entries") + } + + for _, entry := range model.Entries { + param := new(IPSetParam) + param.SetOpcode(opcode) + param.SetName(*model.Name) + param.SetKind(string(*model.Type)) + err := param.BuildMember(entry) + if err != nil { + return fmt.Errorf("Parse ipset member %v failed: %v", entry.Entry, err) + } + *o = append(*o, *param) + } + return nil +} + +// o.kind must be filled before calling BuildMember +func (o *IPSetParam) BuildMember(model *models.IpsetMember) error { + kind := o.getKind() + setType := IPSetTypeGet(models.IpsetType(kind)) + if setType == nil { + return fmt.Errorf("Unsupported ipset type %q", kind) + } + + if model.Entry == nil { + return fmt.Errorf("Empty ipset member entry") + } + + o.SetComment(model.Comment) + if model.Options != nil { + if model.Options.Force != nil && *model.Options.Force { + o.AddFlag(IPSET_F_FORCE) + } else { + o.DelFlag(IPSET_F_FORCE) + } + if model.Options.NoMatch != nil { + o.SetNomatch(*model.Options.NoMatch) + } + } + + return setType.ParseEntry(*model.Entry, o) +} + +func (o *IPSetParam) Check() error { + if o.opcode >= IPSET_OP_MAX { + return fmt.Errorf("Invalid ipset opcode %v", o.opcode) + } else if o.opcode == IPSET_OP_LIST { + return nil + } else if o.opcode == IPSET_OP_TEST { + if o.cidr > 0 || o.cidr2 > 0 { + return fmt.Errorf("Cidr set in IPSET_OP_TEST (IsIn)") + } + } + + kind := o.getKind() + setType := IPSetTypeGet(models.IpsetType(kind)) + if setType == nil { + return fmt.Errorf("Unsupported ipset type %q", kind) + } + + startIP1, endIP1, startPort1, endPort1 := o.addrRange.Decode(o.af) + startIP2, endIP2, startPort2, endPort2 := o.addrRange2.Decode(o.af) + if o.af == syscall.AF_INET6 { + if !endIP1.Equal(net.IPv6zero) && !endIP1.Equal(startIP1) { + return fmt.Errorf("IPv6 range is not supported") + } + if !endIP2.Equal(net.IPv6zero) && !endIP2.Equal(startIP2) { + return fmt.Errorf("IPv6 range is not supported") + } + } else if o.af == syscall.AF_INET { + start, end := ip4ToUint32(startIP1), ip4ToUint32(endIP1) + if end != 0 && start > end { + return fmt.Errorf("Invalid IPv4 range: %v-%v", startIP1, endIP1) + } + start, end = ip4ToUint32(startIP2), ip4ToUint32(endIP2) + if end != 0 && start > end { + return fmt.Errorf("Invalid IPv4 range: %v-%v", startIP2, endIP2) + } + } + + if endPort1 > 0 && startPort1 > endPort1 { + return fmt.Errorf("Invalid port range: %d-%d", startPort1, endPort1) + } + + if endPort2 > 0 && startPort2 > endPort2 { + return fmt.Errorf("Invalid port range: %d-%d", startPort2, endPort2) + } + + return setType.CheckParam(o) +} + +func (o *IPSetParamArray) Check() error { + for _, param := range *o { + if err := param.Check(); err != nil { + return err + } + } + return nil +} + +func (o *IPSetMember) Model(af uint8, kind models.IpsetType) (*models.IpsetMember, error) { + setType := IPSetTypeGet(kind) + if setType == nil { + return nil, fmt.Errorf("Unsupported ipset type %q", kind) + } + + model, err := setType.ModelEntry(af, o) + if err != nil { + return nil, err + } + + model.Comment = o.GetComment() + nomatch := o.GetNoMatch() + if nomatch { + model.Options = &models.IpsetOption{} + model.Options.NoMatch = &nomatch + } + return model, nil +} + +func (o *IPSetInfo) Model() (*models.IpsetInfo, error) { + model := new(models.IpsetInfo) + model.Name = new(string) + model.Type = new(models.IpsetType) + model.CreationOptions = new(models.IpsetCreationOption) + + *model.Name = o.GetName() + *model.Type = models.IpsetType(o.GetKind()) + model.Opcode = IPSET_OP_LIST + + af := o.GetAf() + cidr := o.GetCidr() + withIP := false + + copts := model.CreationOptions + if o.GetComment() { + copts.Comment = new(bool) + *copts.Comment = true + } + copts.Family = afString(af) + if strings.HasPrefix(string(*model.Type), "hash:") { + copts.HashMaxElem = o.GetHashMaxElem() + copts.HashSize = o.GetHashSize() + } else if strings.HasPrefix(string(*model.Type), "bitmap:") { + copts.Range = "" + startIP, endIP, startPort, endPort := o.GetAddrRange() + if cidr > 0 { + copts.Range += fmt.Sprintf("%s/%d", startIP, cidr) + withIP = true + } else if af == syscall.AF_INET { + startIPNum, endIPNum := ip4ToUint32(startIP), ip4ToUint32(endIP) + if endIPNum > startIPNum { + copts.Range += fmt.Sprintf("%s-%s", startIP.String(), endIP.String()) + withIP = true + } + } + if endPort > startPort { + if withIP { + copts.Range += ":" + } + copts.Range += fmt.Sprintf("%d-%d", startPort, endPort) + } + } + + for _, member := range o.GetMembers() { + memberModel, err := member.Model(af, *model.Type) + if err != nil { + return nil, err + } + model.Entries = append(model.Entries, memberModel) + } + + return model, nil +} + +func (o *IPSetInfoArray) Model() (*models.IpsetInfoArray, error) { + model := new(models.IpsetInfoArray) + for _, info := range o.GetIPSetInfos() { + infoModel, err := info.Model() + if err != nil { + return nil, err + } + model.Infos = append(model.Infos, infoModel) + } + model.Count = int32(len(model.Infos)) + + return model, nil +} diff --git a/tools/dpvs-agent/pkg/ipc/types/snapshot.go b/tools/dpvs-agent/pkg/ipc/types/snapshot.go new file mode 100644 index 000000000..28b713b08 --- /dev/null +++ b/tools/dpvs-agent/pkg/ipc/types/snapshot.go @@ -0,0 +1,257 @@ +package types + +import ( + "encoding/json" + "fmt" + "net" + "os" + "strconv" + "strings" + "sync" + "time" + + "golang.org/x/sys/unix" + + "github.com/dpvs-agent/models" + "github.com/hashicorp/go-hclog" +) + +type ServiceSnapshot struct { + Service *models.VirtualServerSpecExpand + lock *sync.RWMutex +} + +type NodeSnapshot struct { + NodeSpec *models.DpvsNodeSpec + Snapshot map[string]*ServiceSnapshot +} + +func (snap *ServiceSnapshot) Lock() { + snap.lock.Lock() +} + +func (snap *ServiceSnapshot) Unlock() { + snap.lock.Unlock() +} + +func (snap *ServiceSnapshot) RLock() { + snap.lock.RLock() +} + +func (snap *ServiceSnapshot) RUnlock() { + snap.lock.RUnlock() +} + +func (node *NodeSnapshot) SnapshotID(id string) string { + items := strings.Split(id, "-") + if len(items) != 3 { + return "" + } + + proto := items[2] + svcProto := "tcp" + switch strings.ToLower(proto) { + case "udp", "tcp": + svcProto = strings.ToLower(proto) + default: + return "" + } + + port, err := strconv.Atoi(items[1]) + if err != nil { + return "" + } + vsPort := uint16(port) + + vip := net.ParseIP(items[0]) + if vip == nil { + return "" + } + + return fmt.Sprintf("%s-%d-%s", strings.ToLower(vip.String()), vsPort, svcProto) +} + +func (node *NodeSnapshot) ServiceRLock(id string) bool { + snapID := node.SnapshotID(id) + + snap, exist := node.Snapshot[strings.ToLower(snapID)] + if exist { + snap.RLock() + } + + return exist +} + +func (node *NodeSnapshot) ServiceRUnlock(id string) { + snapID := node.SnapshotID(id) + if snap, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + snap.RUnlock() + } +} + +func (node *NodeSnapshot) ServiceLock(id string) bool { + snapID := node.SnapshotID(id) + snap, exist := node.Snapshot[strings.ToLower(snapID)] + if exist { + snap.Lock() + } + + return exist +} + +func (node *NodeSnapshot) ServiceUnlock(id string) { + snapID := node.SnapshotID(id) + if snap, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + snap.Unlock() + } +} + +func (node *NodeSnapshot) ServiceVersionUpdate(id string, logger hclog.Logger) { + snapID := node.SnapshotID(id) + snapshot := node.Snapshot + logger.Info("Update server version begin.", "id", id, "services snapshot", snapshot) + if _, exist := snapshot[strings.ToLower(snapID)]; exist { + expireVersion := snapshot[strings.ToLower(snapID)].Service.Version + snapshot[strings.ToLower(snapID)].Service.Version = strconv.FormatInt(time.Now().UnixNano()/1e6, 10) + latestVersion := snapshot[strings.ToLower(snapID)].Service.Version + + logger.Info("Service version update done.", "expireVersion", expireVersion, "latest Version", latestVersion) + return + } + logger.Error("Update service version failed. Service not Exist.", "id", id) +} + +func (node *NodeSnapshot) SnapshotGet(id string) *ServiceSnapshot { + snapID := node.SnapshotID(id) + if snap, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + return snap + } + return nil +} + +func (node *NodeSnapshot) ServiceGet(id string) *models.VirtualServerSpecExpand { + snapID := node.SnapshotID(id) + if snap, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + return snap.Service + } + return nil +} + +func (node *NodeSnapshot) ServiceDel(id string) { + snapID := node.SnapshotID(id) + if _, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + delete(node.Snapshot, strings.ToLower(snapID)) + } +} + +func (node *NodeSnapshot) ServiceVersion(id string) string { + snapID := node.SnapshotID(id) + if _, exist := node.Snapshot[strings.ToLower(snapID)]; exist { + return node.Snapshot[strings.ToLower(snapID)].Service.Version + } + return strconv.FormatInt(time.Now().UnixNano()/1e6, 10) +} + +func (node *NodeSnapshot) ServiceAdd(vs *VirtualServerSpec) { + version := node.ServiceVersion(vs.ID()) + + svc := vs.GetModel() + svc.Version = version + if svc.RSs == nil { + svc.RSs = &models.RealServerExpandList{Items: make([]*models.RealServerSpecExpand, 0)} + } + + node.Snapshot[strings.ToLower(vs.ID())] = &ServiceSnapshot{Service: svc, lock: new(sync.RWMutex)} +} + +func (node *NodeSnapshot) ServiceUpsert(spec *models.VirtualServerSpecExpand) { + svc := (*VirtualServerSpecExpandModel)(spec) + + version := node.ServiceVersion(svc.ID()) + + if _, exist := node.Snapshot[strings.ToLower(svc.ID())]; !exist { + node.Snapshot[strings.ToLower(svc.ID())] = &ServiceSnapshot{Service: spec, lock: new(sync.RWMutex)} + } else { + node.Snapshot[strings.ToLower(svc.ID())].Service = spec + } + + node.Snapshot[strings.ToLower(svc.ID())].Service.Version = version +} + +func (node *NodeSnapshot) GetModels(logger hclog.Logger) *models.VirtualServerList { + services := &models.VirtualServerList{Items: make([]*models.VirtualServerSpecExpand, len(node.Snapshot))} + i := 0 + for _, snap := range node.Snapshot { + services.Items[i] = snap.Service + i++ + } + + logger.Info("services", services) + return services +} + +type RealServerSpecExpandModel models.RealServerSpecExpand + +func (rs *RealServerSpecExpandModel) ID() string { + return fmt.Sprintf("%s:%d", net.ParseIP(rs.Spec.IP), rs.Spec.Port) +} + +type VirtualServerSpecExpandModel models.VirtualServerSpecExpand + +func (spec *VirtualServerSpecExpandModel) ID() string { + proto := "tcp" + if spec.Proto == unix.IPPROTO_UDP { + proto = "udp" + } + + return fmt.Sprintf("%s-%d-%s", net.ParseIP(spec.Addr).String(), spec.Port, proto) +} + +func (node *NodeSnapshot) LoadFrom(cacheFile string, logger hclog.Logger) error { + content, err := os.ReadFile(cacheFile) + if err != nil { + logger.Error("Read dpvs service cache file failed.", "Error", err.Error()) + return err + } + var nodeSnapshot models.NodeServiceSnapshot + if err := json.Unmarshal(content, &nodeSnapshot); err != nil { + logger.Error("Deserialization Failed.", "content", content, "Error", err.Error()) + return err + } + + node.NodeSpec = nodeSnapshot.NodeSpec + for _, svcModel := range nodeSnapshot.Services.Items { + svc := (*VirtualServerSpecExpandModel)(svcModel) + node.Snapshot[strings.ToLower(svc.ID())].Service = svcModel + } + + return nil +} + +func (node *NodeSnapshot) DumpTo(cacheFile string, logger hclog.Logger) error { + nodeSnapshot := &models.NodeServiceSnapshot{ + NodeSpec: node.NodeSpec, + Services: node.GetModels(logger), + } + + content, err := json.Marshal(nodeSnapshot) + if err != nil { + logger.Error(err.Error()) + return err + } + + nowStr := time.Now().Format("2006-01-02 15:04:05") + nowStr = strings.ReplaceAll(nowStr, " ", "+") + bakName := cacheFile + nowStr + if err := os.Rename(cacheFile, bakName); err != nil { + logger.Error(err.Error()) + return err + } + + if err := os.WriteFile(cacheFile, []byte(content), 0644); err != nil { + logger.Error(err.Error()) + return err + } + + return nil +} diff --git a/tools/dpvs-agent/pkg/ipc/types/virtualserver.go b/tools/dpvs-agent/pkg/ipc/types/virtualserver.go index 1dc99f35a..e850946c9 100644 --- a/tools/dpvs-agent/pkg/ipc/types/virtualserver.go +++ b/tools/dpvs-agent/pkg/ipc/types/virtualserver.go @@ -289,6 +289,10 @@ func (vs *VirtualServerSpec) SetFlagsExpireQuiescent() { vs.setFlags(DPVS_SVC_F_EXPIRE_QUIESCENT) } +func (vs *VirtualServerSpec) SetFlagsQuic() { + vs.setFlags(DPVS_SVC_F_QUIC) +} + func (vs *VirtualServerSpec) SetFlagsPersistent() { vs.setFlags(DPVS_SVC_F_PERSISTENT) } diff --git a/tools/dpvs-agent/pkg/settings/settings.go b/tools/dpvs-agent/pkg/settings/settings.go new file mode 100644 index 000000000..218e8302f --- /dev/null +++ b/tools/dpvs-agent/pkg/settings/settings.go @@ -0,0 +1,43 @@ +package settings + +import ( + "sync" + + "github.com/dpvs-agent/models" + "github.com/dpvs-agent/pkg/ipc/types" +) + +var ( + shareSnapshot *types.NodeSnapshot + shareAppConfig *AppConfig + initOnce sync.Once +) + +type AppConfig struct { + CacheFile string +} + +func setUp() { + shareAppConfig = &AppConfig{} + shareSnapshot = &types.NodeSnapshot{ + NodeSpec: &models.DpvsNodeSpec{ + AnnouncePort: &models.VsAnnouncePort{}, + }, + Snapshot: make(map[string]*types.ServiceSnapshot), + } +} + +func ShareAppConfig() *AppConfig { + initOnce.Do(setUp) + return shareAppConfig +} + +func ShareSnapshot() *types.NodeSnapshot { + initOnce.Do(setUp) + return shareSnapshot +} + +func LocalConfigFile() string { + // return filepath.Join(shareAppConfig.ConfigDir, "cache") + return shareAppConfig.CacheFile +} diff --git a/tools/dpvs-agent/pkg/settings/util.go b/tools/dpvs-agent/pkg/settings/util.go new file mode 100644 index 000000000..73296b7de --- /dev/null +++ b/tools/dpvs-agent/pkg/settings/util.go @@ -0,0 +1,18 @@ +package settings + +import ( + "errors" + "fmt" + "strings" +) + +func MergedError(errs []error) error { + if len(errs) <= 0 { + return nil + } + var msg []string + for _, e := range errs { + msg = append(msg, e.Error()) + } + return errors.New(fmt.Sprintf("errors: %s", strings.Join(msg, "\n"))) +} diff --git a/tools/dpvs-agent/restapi/embedded_spec.go b/tools/dpvs-agent/restapi/embedded_spec.go index 7c6773a97..323f0a90c 100644 --- a/tools/dpvs-agent/restapi/embedded_spec.go +++ b/tools/dpvs-agent/restapi/embedded_spec.go @@ -89,6 +89,9 @@ func init() { ], "summary": "add/update special net device ip addr", "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, { "$ref": "#/parameters/sapool" }, @@ -265,8 +268,11 @@ func init() { "tags": [ "device" ], - "summary": "ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device", + "summary": "ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device\n", "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, { "$ref": "#/parameters/device-name" }, @@ -293,7 +299,7 @@ func init() { "tags": [ "device" ], - "summary": "ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device", + "summary": "ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device\n", "parameters": [ { "$ref": "#/parameters/device-name" @@ -354,7 +360,7 @@ func init() { "tags": [ "device" ], - "summary": "dpip link set ${nic-name} [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down]", + "summary": "dpip link set ${nic-name} [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down]\n", "parameters": [ { "$ref": "#/parameters/device-name" @@ -560,72 +566,81 @@ func init() { } } }, - "/vs": { + "/ipset": { "get": { "tags": [ - "virtualserver" - ], - "summary": "display all vip:port:proto and rsip:port list", - "parameters": [ - { - "$ref": "#/parameters/stats" - } + "ipset" ], + "summary": "Get all the ipsets and members.", + "operationId": "GetAll", "responses": { "200": { "description": "Success", "schema": { - "$ref": "#/definitions/VirtualServerList" + "$ref": "#/definitions/IpsetInfoArray" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } } }, - "/vs/{VipPort}": { + "/ipset/{name}": { "get": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "get a specific virtual server", + "summary": "Get a specific ipset and its members.", + "operationId": "Get", "parameters": [ { - "$ref": "#/parameters/service-id" - }, - { - "$ref": "#/parameters/stats" + "$ref": "#/parameters/ipset-name" } ], "responses": { "200": { "description": "Success", "schema": { - "$ref": "#/definitions/VirtualServerList" + "$ref": "#/definitions/IpsetInfo" } }, "404": { - "description": "Service not found", + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } }, "put": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "create or update virtual server", + "summary": "Create an ipset named {name}.", + "operationId": "Create", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" }, { - "$ref": "#/parameters/vs-config" + "$ref": "#/parameters/ipset-param" } ], "responses": { "200": { - "description": "Updated", + "description": "Replaced", "schema": { "type": "string" } @@ -636,24 +651,22 @@ func init() { "type": "string" } }, - "460": { - "description": "Invalid frontend in service configuration", + "400": { + "description": "Invalid ipset parameter", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidFrontend" + "type": "string" + } }, - "461": { - "description": "Invalid backend in service configuration", + "404": { + "description": "Ipset not found", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" + "type": "string" + } }, "500": { - "description": "Error while creating virtual server", + "description": "Service not available", "schema": { - "$ref": "#/definitions/Error" + "type": "string" }, "x-go-name": "Failure" } @@ -661,106 +674,131 @@ func init() { }, "delete": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "Delete a vip:port:proto", + "summary": "Delete the ipset named {name}.", + "operationId": "Destroy", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" } ], "responses": { "200": { - "description": "Success", + "description": "Deleted", "schema": { "type": "string" } }, "404": { - "description": "Service not found", + "description": "Ipset not found", "schema": { "type": "string" } }, "500": { - "description": "Service deletion failed", + "description": "Service not available", "schema": { - "$ref": "#/definitions/Error" + "type": "string" }, "x-go-name": "Failure" } } } }, - "/vs/{VipPort}/allow": { - "get": { + "/ipset/{name}/cell": { + "post": { "tags": [ - "virtualserver" + "ipset" ], + "summary": "Check if an object in the ipset.", + "operationId": "IsIn", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" + }, + { + "$ref": "#/parameters/ipset-cell" } ], "responses": { "200": { - "description": "Success", + "description": "Succeed", + "schema": { + "type": "object", + "required": [ + "Result" + ], + "properties": { + "Message": { + "type": "string" + }, + "Result": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "Invalid ipset parameter", "schema": { "type": "string" } }, "404": { - "description": "Service not found", + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } - }, + } + }, + "/ipset/{name}/member": { "put": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "Add a set of ip from white list to vip:port:proto", + "summary": "Reset the whole ipset members.", + "operationId": "ReplaceMember", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" }, { - "$ref": "#/parameters/acl-config" + "$ref": "#/parameters/ipset-param" } ], "responses": { "200": { - "description": "Success", + "description": "Succeed", "schema": { "type": "string" } }, - "201": { - "description": "Created", + "400": { + "description": "Invalid ipset parameter", "schema": { "type": "string" } }, - "460": { - "description": "Invalid frontend in service configuration", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidFrontend" - }, - "461": { - "description": "Invalid backend in service configuration", + "404": { + "description": "Ipset not found", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" + "type": "string" + } }, "500": { - "description": "Service deletion failed", + "description": "Service not available", "schema": { - "$ref": "#/definitions/Error" + "type": "string" }, "x-go-name": "Failure" } @@ -768,20 +806,21 @@ func init() { }, "post": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "Update a fully white ip list to vip:port:proto", + "summary": "Add members to the ipset.", + "operationId": "AddMember", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" }, { - "$ref": "#/parameters/acl-config" + "$ref": "#/parameters/ipset-param" } ], "responses": { "200": { - "description": "Success", + "description": "Existed", "schema": { "type": "string" } @@ -792,24 +831,22 @@ func init() { "type": "string" } }, - "460": { - "description": "Invalid frontend in service configuration", + "400": { + "description": "Invalid ipset parameter", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidFrontend" + "type": "string" + } }, - "461": { - "description": "Invalid backend in service configuration", + "404": { + "description": "Ipset not found", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" + "type": "string" + } }, "500": { - "description": "Service deletion failed", + "description": "Service not available", "schema": { - "$ref": "#/definitions/Error" + "type": "string" }, "x-go-name": "Failure" } @@ -817,66 +854,106 @@ func init() { }, "delete": { "tags": [ - "virtualserver" + "ipset" ], - "summary": "Delete a set of ip form white list to vip:port:proto", + "summary": "Delete members from the ipset.", + "operationId": "DelMember", "parameters": [ { - "$ref": "#/parameters/service-id" + "$ref": "#/parameters/ipset-name" }, { - "$ref": "#/parameters/acl-config" + "$ref": "#/parameters/ipset-param" } ], "responses": { "200": { - "description": "Success" + "description": "Succeed", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid ipset parameter", + "schema": { + "type": "string" + } }, "404": { - "description": "Service not found", + "description": "Ipset not found", "schema": { "type": "string" } }, - "460": { - "description": "Invalid frontend in service configuration", + "500": { + "description": "Service not available", "schema": { - "$ref": "#/definitions/Error" + "type": "string" }, - "x-go-name": "InvalidFrontend" + "x-go-name": "Failure" + } + } + } + }, + "/vs": { + "get": { + "tags": [ + "virtualserver" + ], + "summary": "display all vip:port:proto and rsip:port list", + "parameters": [ + { + "$ref": "#/parameters/stats" }, - "461": { - "description": "Invalid backend in service configuration", + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/healthcheck" + } + ], + "responses": { + "200": { + "description": "Success", "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" + "$ref": "#/definitions/VirtualServerList" + } }, - "500": { - "description": "Service deletion failed", + "204": { + "description": "No Content", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/VirtualServerList" }, - "x-go-name": "Failure" + "x-go-name": "NoContent" } } } }, - "/vs/{VipPort}/deny": { + "/vs/{VipPort}": { "get": { "tags": [ "virtualserver" ], + "summary": "get a specific virtual server", "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/healthcheck" + }, { "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/stats" } ], "responses": { "200": { "description": "Success", "schema": { - "type": "string" + "$ref": "#/definitions/VirtualServerList" } }, "404": { @@ -891,18 +968,21 @@ func init() { "tags": [ "virtualserver" ], - "summary": "Add a set of ip from black list to vip:port:proto", + "summary": "create or update virtual server", "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/acl-config" + "$ref": "#/parameters/vs-config" } ], "responses": { "200": { - "description": "Success", + "description": "Updated", "schema": { "type": "string" } @@ -928,7 +1008,7 @@ func init() { "x-go-name": "InvalidBackend" }, "500": { - "description": "Service deletion failed", + "description": "Error while creating virtual server", "schema": { "$ref": "#/definitions/Error" }, @@ -936,17 +1016,14 @@ func init() { } } }, - "post": { + "delete": { "tags": [ "virtualserver" ], - "summary": "Update a fully black ip list to vip:port:proto", + "summary": "Delete a vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" - }, - { - "$ref": "#/parameters/acl-config" } ], "responses": { @@ -956,26 +1033,12 @@ func init() { "type": "string" } }, - "201": { - "description": "Created", + "404": { + "description": "Service not found", "schema": { "type": "string" } }, - "460": { - "description": "Invalid frontend in service configuration", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidFrontend" - }, - "461": { - "description": "Invalid backend in service configuration", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" - }, "500": { "description": "Service deletion failed", "schema": { @@ -984,92 +1047,93 @@ func init() { "x-go-name": "Failure" } } - }, - "delete": { + } + }, + "/vs/{VipPort}/allow": { + "get": { "tags": [ "virtualserver" ], - "summary": "Delete a set of ip form black list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" - }, - { - "$ref": "#/parameters/acl-config" } ], "responses": { "200": { - "description": "Success" + "description": "Success", + "schema": { + "type": "string" + } }, "404": { "description": "Service not found", "schema": { "type": "string" } - }, - "460": { - "description": "Invalid frontend in service configuration", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidFrontend" - }, - "461": { - "description": "Invalid backend in service configuration", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "InvalidBackend" - }, - "500": { - "description": "Service deletion failed", - "schema": { - "$ref": "#/definitions/Error" - }, - "x-go-name": "Failure" } } - } - }, - "/vs/{VipPort}/laddr": { - "get": { + }, + "put": { "tags": [ "virtualserver" ], + "summary": "Add a set of ip from white list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/stats" + "$ref": "#/parameters/acl-config" } ], "responses": { "200": { "description": "Success", "schema": { - "$ref": "#/definitions/LocalAddressExpandList" + "type": "string" } }, - "404": { - "description": "Service not found", + "201": { + "description": "Created", "schema": { "type": "string" } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" } } }, - "put": { + "post": { "tags": [ "virtualserver" ], + "summary": "Update a fully white ip list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/laddr-config" + "$ref": "#/parameters/acl-config" } ], "responses": { @@ -1100,7 +1164,7 @@ func init() { "x-go-name": "InvalidBackend" }, "500": { - "description": "Error while creating virtual server", + "description": "Service deletion failed", "schema": { "$ref": "#/definitions/Error" }, @@ -1112,17 +1176,21 @@ func init() { "tags": [ "virtualserver" ], + "summary": "Delete a set of ip form white list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/laddr-config" + "$ref": "#/parameters/acl-config" } ], "responses": { "200": { - "description": "Success", + "description": "Success" + }, + "404": { + "description": "Service not found", "schema": { "type": "string" } @@ -1142,7 +1210,7 @@ func init() { "x-go-name": "InvalidBackend" }, "500": { - "description": "Error while creating virtual server", + "description": "Service deletion failed", "schema": { "$ref": "#/definitions/Error" }, @@ -1151,7 +1219,7 @@ func init() { } } }, - "/vs/{VipPort}/rs": { + "/vs/{VipPort}/deny": { "get": { "tags": [ "virtualserver" @@ -1159,9 +1227,6 @@ func init() { "parameters": [ { "$ref": "#/parameters/service-id" - }, - { - "$ref": "#/parameters/stats" } ], "responses": { @@ -1183,16 +1248,13 @@ func init() { "tags": [ "virtualserver" ], - "summary": "Add a set of real server to vip:port:proto", + "summary": "Add a set of ip from black list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/rss-config" - }, - { - "$ref": "#/parameters/healthcheck" + "$ref": "#/parameters/acl-config" } ], "responses": { @@ -1235,13 +1297,13 @@ func init() { "tags": [ "virtualserver" ], - "summary": "Update fully real server list to vip:port:proto", + "summary": "Update a fully black ip list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/rss-config" + "$ref": "#/parameters/acl-config" } ], "responses": { @@ -1284,13 +1346,13 @@ func init() { "tags": [ "virtualserver" ], - "summary": "Delete a set of real server from vip:port:proto", + "summary": "Delete a set of ip form black list to vip:port:proto", "parameters": [ { "$ref": "#/parameters/service-id" }, { - "$ref": "#/parameters/rss-config" + "$ref": "#/parameters/acl-config" } ], "responses": { @@ -1326,62 +1388,599 @@ func init() { } } } - } - }, - "definitions": { - "AclAddrList": { - "type": "object", - "properties": { - "Items": { - "type": "array", - "items": { - "$ref": "#/definitions/CertAuthSpec" + }, + "/vs/{VipPort}/laddr": { + "get": { + "tags": [ + "virtualserver" + ], + "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/stats" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/LocalAddressExpandList" + } + }, + "404": { + "description": "Service not found", + "schema": { + "type": "string" + } } } - } - }, - "AddrRange": { - "type": "object", - "properties": { - "End": { - "type": "string" - }, - "Start": { - "type": "string" + }, + "put": { + "tags": [ + "virtualserver" + ], + "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/laddr-config" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Created", + "schema": { + "type": "string" + } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Error while creating virtual server", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } } - } - }, - "CertAuthSpec": { - "properties": { - "addr": { - "type": "string" + }, + "delete": { + "tags": [ + "virtualserver" + ], + "parameters": [ + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/laddr-config" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Error while creating virtual server", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } } } }, - "DestCheckSpec": { - "type": "string", - "enum": [ - "passive", - "tcp", - "udp", - "ping" - ] - }, - "Error": { - "type": "string" - }, - "InetAddrSpec": { - "properties": { - "addr": { - "type": "string" - }, - "broadcast": { - "type": "string" - }, - "scope": { - "type": "string" - } - } + "/vs/{VipPort}/rs": { + "get": { + "tags": [ + "virtualserver" + ], + "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/stats" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Service not found", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "tags": [ + "virtualserver" + ], + "summary": "Add a set of real server to vip:port:proto", + "parameters": [ + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/rss-config" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Created", + "schema": { + "type": "string" + } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } + } + }, + "post": { + "tags": [ + "virtualserver" + ], + "summary": "Update fully real server list to vip:port:proto", + "parameters": [ + { + "$ref": "#/parameters/snapshot" + }, + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/rss-config" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Created", + "schema": { + "type": "string" + } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } + } + }, + "delete": { + "tags": [ + "virtualserver" + ], + "summary": "Delete a set of real server from vip:port:proto", + "parameters": [ + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/rss-config" + } + ], + "responses": { + "200": { + "description": "Success" + }, + "404": { + "description": "Service not found", + "schema": { + "type": "string" + } + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } + } + } + }, + "/vs/{VipPort}/rs/health": { + "put": { + "tags": [ + "virtualserver" + ], + "summary": "dpvs healthcheck update rs weight", + "parameters": [ + { + "$ref": "#/parameters/version" + }, + { + "$ref": "#/parameters/service-id" + }, + { + "$ref": "#/parameters/rss-config" + } + ], + "responses": { + "200": { + "description": "Success" + }, + "270": { + "description": "the rss-config parameter is outdated, update nothing and return the latest rs info\n", + "schema": { + "$ref": "#/definitions/VirtualServerSpecExpand" + }, + "x-go-name": "Unexpected" + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } + } + } + } + }, + "definitions": { + "AclAddrList": { + "type": "object", + "properties": { + "Items": { + "type": "array", + "items": { + "$ref": "#/definitions/CertAuthSpec" + } + } + } + }, + "AddrRange": { + "type": "object", + "properties": { + "End": { + "type": "string" + }, + "Start": { + "type": "string" + } + } + }, + "CertAuthSpec": { + "properties": { + "addr": { + "type": "string" + }, + "ipset": { + "type": "string" + } + } + }, + "DestCheckSpec": { + "type": "string", + "enum": [ + "passive", + "tcp", + "udp", + "ping", + "udpping", + "http" + ] + }, + "DpvsNodeSpec": { + "type": "object", + "properties": { + "AnnouncePort": { + "$ref": "#/definitions/VsAnnouncePort" + }, + "Laddrs": { + "$ref": "#/definitions/LocalAddressExpandList" + } + } + }, + "Error": { + "type": "string" + }, + "InetAddrSpec": { + "properties": { + "addr": { + "type": "string" + }, + "broadcast": { + "type": "string" + }, + "scope": { + "type": "string" + } + } + }, + "IpsetCell": { + "description": "IpsetCell represents an indivisible granularity of ipset member.", + "type": "object", + "required": [ + "Type", + "Member" + ], + "properties": { + "Member": { + "$ref": "#/definitions/IpsetMember" + }, + "Type": { + "$ref": "#/definitions/IpsetType" + } + } + }, + "IpsetCreationOption": { + "description": "IpsetCreationOption contains all available options required in creating an ipset.\n", + "properties": { + "Comment": { + "type": "boolean", + "default": false + }, + "Family": { + "type": "string", + "enum": [ + "ipv4", + "ipv6" + ] + }, + "HashMaxElem": { + "type": "integer", + "format": "uint32" + }, + "HashSize": { + "type": "integer", + "format": "uint32" + }, + "Range": { + "description": "vaild format: ipv4-ipv4, ipv4/pfx, ipv6/pfx, port-port\n", + "type": "string" + } + } + }, + "IpsetInfo": { + "description": "IpsetInfo contains all parameters and information for ipset operations.\n", + "type": "object", + "required": [ + "Type", + "Name" + ], + "properties": { + "CreationOptions": { + "$ref": "#/definitions/IpsetCreationOption" + }, + "Entries": { + "type": "array", + "items": { + "$ref": "#/definitions/IpsetMember" + } + }, + "Name": { + "type": "string", + "maxLength": 32, + "minLength": 1 + }, + "Opcode": { + "description": "opertaion type code", + "type": "integer", + "format": "uint16", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "x-enum-varnames": [ + "Add", + "Del", + "Test", + "Create", + "Destroy", + "Flush", + "List" + ] + }, + "Type": { + "type": "string", + "$ref": "#/definitions/IpsetType" + } + } + }, + "IpsetInfoArray": { + "description": "IpsetInfoArray contains an array of ipset.", + "type": "object", + "properties": { + "Count": { + "type": "integer", + "format": "int32" + }, + "Infos": { + "type": "array", + "items": { + "$ref": "#/definitions/IpsetInfo" + } + } + } + }, + "IpsetMember": { + "description": "IpsetMember represents a specific entry in ipset.", + "type": "object", + "required": [ + "Entry" + ], + "properties": { + "Comment": { + "type": "string", + "maxLength": 32, + "minLength": 1 + }, + "Entry": { + "description": "type specific entry data, for example\n* 192.168.1.0/29 (bitmap:ip)\n* 192.168.88.0/24,tcp:8080-8082 (hash:net)\n* 2001::1,8080-8082,2002::aaaa:bbbb:ccc0:0/108 (hash:ip,port,net)\n", + "type": "string" + }, + "Options": { + "$ref": "#/definitions/IpsetOption" + } + } + }, + "IpsetOption": { + "description": "IpsetOption defines common options for ipset operations.", + "type": "object", + "properties": { + "Force": { + "description": "When add members to ipset with Force set, the already existing members are replaced sliently instead of emitting an EDPVS_EXIST error; When delete non-existent memebers from ipset, the DPSVS_NOTEXIST error is ignored.\n", + "type": "boolean", + "default": false + }, + "NoMatch": { + "description": "Nomatch excludes a small element range from an ipset, which is mainly used by network-cidr based ipset.\n", + "type": "boolean", + "default": false + } + } + }, + "IpsetType": { + "type": "string", + "enum": [ + "bitmap:ip", + "bitmap:ip,mac", + "bitmap:port", + "hash:ip", + "hash:net", + "hash:ip,port", + "hash:net,port", + "hash:net,port,iface", + "hash:ip,port,ip", + "hash:ip,port,net", + "hash:net,port,net", + "hash:net,port,net,port" + ] }, "LocalAddressExpandList": { "properties": { @@ -1618,6 +2217,17 @@ func init() { } } }, + "NodeServiceSnapshot": { + "type": "object", + "properties": { + "NodeSpec": { + "$ref": "#/definitions/DpvsNodeSpec" + }, + "Services": { + "$ref": "#/definitions/VirtualServerList" + } + } + }, "RealServerExpandList": { "type": "object", "properties": { @@ -1862,6 +2472,13 @@ func init() { 18 ] }, + "Quic": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, "RSs": { "$ref": "#/definitions/RealServerExpandList" }, @@ -1887,6 +2504,9 @@ func init() { "Timeout": { "type": "integer", "format": "uint32" + }, + "Version": { + "type": "string" } } }, @@ -1926,6 +2546,10 @@ func init() { "disable" ] }, + "Quic": { + "type": "boolean", + "default": false + }, "SchedName": { "type": "string", "enum": [ @@ -1957,6 +2581,17 @@ func init() { "type": "string" } } + }, + "VsAnnouncePort": { + "type": "object", + "properties": { + "dpvs": { + "type": "string" + }, + "switch": { + "type": "string" + } + } } }, "parameters": { @@ -1997,6 +2632,26 @@ func init() { "name": "healthcheck", "in": "query" }, + "ipset-cell": { + "name": "ipsetCell", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetCell" + } + }, + "ipset-name": { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + "ipset-param": { + "name": "ipsetParam", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } + }, "laddr-config": { "name": "spec", "in": "body", @@ -2065,6 +2720,12 @@ func init() { "in": "path", "required": true }, + "snapshot": { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, "stats": { "type": "boolean", "default": false, @@ -2077,6 +2738,12 @@ func init() { "name": "verbose", "in": "query" }, + "version": { + "type": "string", + "name": "version", + "in": "query", + "required": true + }, "vlan-config": { "name": "spec", "in": "body", @@ -2124,6 +2791,10 @@ func init() { { "description": "arp", "name": "arp" + }, + { + "description": "ipset", + "name": "ipset" } ] }`)) @@ -2145,18 +2816,154 @@ func init() { "tags": [ "device" ], - "summary": "display all net device list", + "summary": "display all net device list", + "parameters": [ + { + "type": "boolean", + "default": false, + "name": "stats", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + } + } + } + }, + "/device/{name}/addr": { + "get": { + "tags": [ + "device" + ], + "summary": "display special net device ip addr", + "parameters": [ + { + "type": "boolean", + "default": false, + "name": "stats", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "name": "verbose", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "tags": [ + "device" + ], + "summary": "add/update special net device ip addr", + "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "name": "sapool", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + { + "name": "spec", + "in": "body", + "schema": { + "$ref": "#/definitions/InetAddrSpec" + } + } + ], + "responses": { + "200": { + "description": "Update exist ip addr Success", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Add new ip addr Success", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Failed", + "schema": { + "type": "string" + } + } + } + }, + "delete": { + "tags": [ + "device" + ], + "summary": "delete special net device ip addr", "parameters": [ { "type": "boolean", "default": false, - "name": "stats", + "name": "sapool", "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + { + "name": "spec", + "in": "body", + "schema": { + "$ref": "#/definitions/InetAddrSpec" + } } ], "responses": { "200": { - "description": "Success", + "description": "delete ip addr from device Success", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Failed", "schema": { "type": "string" } @@ -2164,30 +2971,116 @@ func init() { } } }, - "/device/{name}/addr": { + "/device/{name}/netlink": { "get": { "tags": [ "device" ], - "summary": "display special net device ip addr", + "summary": "ip link show dev ${name}", "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, { "type": "boolean", "default": false, "name": "stats", "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } }, + "404": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "tags": [ + "device" + ], + "summary": "ip link set ${name} up", + "parameters": [ { - "type": "boolean", - "default": false, - "name": "verbose", - "in": "query" + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + }, + "delete": { + "tags": [ + "device" + ], + "summary": "ip link set ${name} down", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } }, + "500": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + } + }, + "/device/{name}/netlink/addr": { + "get": { + "tags": [ + "device" + ], + "summary": "ip addr show lo: Display special linux net device addr detail", + "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true + }, + { + "type": "boolean", + "default": false, + "name": "stats", + "in": "query" } ], "responses": { @@ -2209,12 +3102,12 @@ func init() { "tags": [ "device" ], - "summary": "add/update special net device ip addr", + "summary": "ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device\n", "parameters": [ { "type": "boolean", - "default": false, - "name": "sapool", + "default": true, + "name": "snapshot", "in": "query" }, { @@ -2233,19 +3126,13 @@ func init() { ], "responses": { "200": { - "description": "Update exist ip addr Success", - "schema": { - "type": "string" - } - }, - "201": { - "description": "Add new ip addr Success", + "description": "Success", "schema": { "type": "string" } }, "500": { - "description": "Failed", + "description": "Not Found", "schema": { "type": "string" } @@ -2256,14 +3143,8 @@ func init() { "tags": [ "device" ], - "summary": "delete special net device ip addr", + "summary": "ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device\n", "parameters": [ - { - "type": "boolean", - "default": false, - "name": "sapool", - "in": "query" - }, { "type": "string", "name": "name", @@ -2280,13 +3161,13 @@ func init() { ], "responses": { "200": { - "description": "delete ip addr from device Success", + "description": "Success", "schema": { "type": "string" } }, "500": { - "description": "Failed", + "description": "Not Found", "schema": { "type": "string" } @@ -2294,12 +3175,52 @@ func init() { } } }, - "/device/{name}/netlink": { + "/device/{name}/nic": { "get": { "tags": [ "device" ], - "summary": "ip link show dev ${name}", + "summary": "dpip link show ${nic-name} -s -v", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "boolean", + "default": false, + "name": "verbose", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "name": "stats", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/NicDeviceSpecList" + } + }, + "500": { + "description": "Failure", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "tags": [ + "device" + ], + "summary": "dpip link set ${nic-name} [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down]\n", "parameters": [ { "type": "string", @@ -2307,11 +3228,74 @@ func init() { "in": "path", "required": true }, + { + "enum": [ + "unset", + "on", + "off" + ], + "type": "string", + "default": "unset", + "name": "forward2Kni", + "in": "query" + }, + { + "enum": [ + "unset", + "up", + "down" + ], + "type": "string", + "default": "unset", + "name": "link", + "in": "query" + }, + { + "enum": [ + "unset", + "on", + "off" + ], + "type": "string", + "default": "unset", + "name": "promisc", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Failure", + "schema": { + "type": "string" + } + } + } + } + }, + "/device/{name}/route": { + "get": { + "tags": [ + "device" + ], + "summary": "display special net device route", + "parameters": [ { "type": "boolean", "default": false, "name": "stats", "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "path", + "required": true } ], "responses": { @@ -2333,24 +3317,37 @@ func init() { "tags": [ "device" ], - "summary": "ip link set ${name} up", + "summary": "add/update special net device route", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true + }, + { + "name": "spec", + "in": "body", + "schema": { + "$ref": "#/definitions/RouteSpec" + } } ], "responses": { "200": { - "description": "Success", + "description": "Update exist route Success", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Add new route Success", "schema": { "type": "string" } }, "500": { - "description": "Not Found", + "description": "Failed", "schema": { "type": "string" } @@ -2361,24 +3358,31 @@ func init() { "tags": [ "device" ], - "summary": "ip link set ${name} down", + "summary": "delete special net device route", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true + }, + { + "name": "spec", + "in": "body", + "schema": { + "$ref": "#/definitions/RouteSpec" + } } ], "responses": { "200": { - "description": "Success", + "description": "delete route Success", "schema": { "type": "string" } }, "500": { - "description": "Not Found", + "description": "Failed", "schema": { "type": "string" } @@ -2386,12 +3390,12 @@ func init() { } } }, - "/device/{name}/netlink/addr": { + "/device/{name}/vlan": { "get": { "tags": [ "device" ], - "summary": "ip addr show lo: Display special linux net device addr detail", + "summary": "display all net device list", "parameters": [ { "type": "string", @@ -2425,7 +3429,7 @@ func init() { "tags": [ "device" ], - "summary": "ip addr add 10.0.0.1/32 dev eth0: Set ip cird to linux net device", + "summary": "add/update special net device ", "parameters": [ { "type": "string", @@ -2437,7 +3441,7 @@ func init() { "name": "spec", "in": "body", "schema": { - "$ref": "#/definitions/InetAddrSpec" + "$ref": "#/definitions/VlanSpec" } } ], @@ -2449,7 +3453,7 @@ func init() { } }, "500": { - "description": "Not Found", + "description": "Failed", "schema": { "type": "string" } @@ -2460,20 +3464,13 @@ func init() { "tags": [ "device" ], - "summary": "ip addr del 10.0.0.1/32 dev eth0: Delete ip cird fron linux net device", + "summary": "delete special net device", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true - }, - { - "name": "spec", - "in": "body", - "schema": { - "$ref": "#/definitions/InetAddrSpec" - } } ], "responses": { @@ -2484,7 +3481,7 @@ func init() { } }, "500": { - "description": "Not Found", + "description": "Failed", "schema": { "type": "string" } @@ -2492,190 +3489,166 @@ func init() { } } }, - "/device/{name}/nic": { + "/ipset": { "get": { "tags": [ - "device" - ], - "summary": "dpip link show ${nic-name} -s -v", - "parameters": [ - { - "type": "string", - "name": "name", - "in": "path", - "required": true - }, - { - "type": "boolean", - "default": false, - "name": "verbose", - "in": "query" - }, - { - "type": "boolean", - "default": false, - "name": "stats", - "in": "query" - } + "ipset" ], + "summary": "Get all the ipsets and members.", + "operationId": "GetAll", "responses": { "200": { "description": "Success", "schema": { - "$ref": "#/definitions/NicDeviceSpecList" + "$ref": "#/definitions/IpsetInfoArray" } }, "500": { - "description": "Failure", + "description": "Service not available", "schema": { "type": "string" - } + }, + "x-go-name": "Failure" } } - }, - "put": { + } + }, + "/ipset/{name}": { + "get": { "tags": [ - "device" + "ipset" ], - "summary": "dpip link set ${nic-name} [forward2kni,link,promisc,tc-ingress,tc-egress] [on/up,off/down]", + "summary": "Get a specific ipset and its members.", + "operationId": "Get", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true - }, - { - "enum": [ - "unset", - "on", - "off" - ], - "type": "string", - "default": "unset", - "name": "forward2Kni", - "in": "query" - }, - { - "enum": [ - "unset", - "up", - "down" - ], - "type": "string", - "default": "unset", - "name": "link", - "in": "query" - }, - { - "enum": [ - "unset", - "on", - "off" - ], - "type": "string", - "default": "unset", - "name": "promisc", - "in": "query" } ], "responses": { "200": { "description": "Success", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } + }, + "404": { + "description": "Ipset not found", "schema": { "type": "string" } }, "500": { - "description": "Failure", + "description": "Service not available", "schema": { "type": "string" - } + }, + "x-go-name": "Failure" } } - } - }, - "/device/{name}/route": { - "get": { + }, + "put": { "tags": [ - "device" + "ipset" ], - "summary": "display special net device route", + "summary": "Create an ipset named {name}.", + "operationId": "Create", "parameters": [ - { - "type": "boolean", - "default": false, - "name": "stats", - "in": "query" - }, { "type": "string", "name": "name", "in": "path", "required": true + }, + { + "name": "ipsetParam", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } } ], "responses": { "200": { - "description": "Success", + "description": "Replaced", + "schema": { + "type": "string" + } + }, + "201": { + "description": "Created", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid ipset parameter", "schema": { "type": "string" } }, "404": { - "description": "Not Found", + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } }, - "put": { + "delete": { "tags": [ - "device" + "ipset" ], - "summary": "add/update special net device route", + "summary": "Delete the ipset named {name}.", + "operationId": "Destroy", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true - }, - { - "name": "spec", - "in": "body", - "schema": { - "$ref": "#/definitions/RouteSpec" - } } ], "responses": { "200": { - "description": "Update exist route Success", + "description": "Deleted", "schema": { "type": "string" } }, - "201": { - "description": "Add new route Success", + "404": { + "description": "Ipset not found", "schema": { "type": "string" } }, "500": { - "description": "Failed", + "description": "Service not available", "schema": { "type": "string" - } + }, + "x-go-name": "Failure" } } - }, - "delete": { + } + }, + "/ipset/{name}/cell": { + "post": { "tags": [ - "device" + "ipset" ], - "summary": "delete special net device route", + "summary": "Check if an object in the ipset.", + "operationId": "IsIn", "parameters": [ { "type": "string", @@ -2684,35 +3657,60 @@ func init() { "required": true }, { - "name": "spec", + "name": "ipsetCell", "in": "body", "schema": { - "$ref": "#/definitions/RouteSpec" + "$ref": "#/definitions/IpsetCell" } } ], "responses": { "200": { - "description": "delete route Success", + "description": "Succeed", + "schema": { + "type": "object", + "required": [ + "Result" + ], + "properties": { + "Message": { + "type": "string" + }, + "Result": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "Invalid ipset parameter", "schema": { "type": "string" } }, - "500": { - "description": "Failed", + "404": { + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } } }, - "/device/{name}/vlan": { - "get": { + "/ipset/{name}/member": { + "put": { "tags": [ - "device" + "ipset" ], - "summary": "display all net device list", + "summary": "Reset the whole ipset members.", + "operationId": "ReplaceMember", "parameters": [ { "type": "string", @@ -2721,32 +3719,47 @@ func init() { "required": true }, { - "type": "boolean", - "default": false, - "name": "stats", - "in": "query" + "name": "ipsetParam", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } } ], "responses": { "200": { - "description": "Success", + "description": "Succeed", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid ipset parameter", "schema": { "type": "string" } }, "404": { - "description": "Not Found", + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } }, - "put": { + "post": { "tags": [ - "device" + "ipset" ], - "summary": "add/update special net device ", + "summary": "Add members to the ipset.", + "operationId": "AddMember", "parameters": [ { "type": "string", @@ -2755,53 +3768,93 @@ func init() { "required": true }, { - "name": "spec", + "name": "ipsetParam", "in": "body", "schema": { - "$ref": "#/definitions/VlanSpec" + "$ref": "#/definitions/IpsetInfo" } } ], "responses": { "200": { - "description": "Success", + "description": "Existed", "schema": { "type": "string" } }, - "500": { - "description": "Failed", + "201": { + "description": "Created", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid ipset parameter", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } }, "delete": { "tags": [ - "device" + "ipset" ], - "summary": "delete special net device", + "summary": "Delete members from the ipset.", + "operationId": "DelMember", "parameters": [ { "type": "string", "name": "name", "in": "path", "required": true + }, + { + "name": "ipsetParam", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } } ], "responses": { "200": { - "description": "Success", + "description": "Succeed", "schema": { "type": "string" } }, - "500": { - "description": "Failed", + "400": { + "description": "Invalid ipset parameter", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Ipset not found", "schema": { "type": "string" } + }, + "500": { + "description": "Service not available", + "schema": { + "type": "string" + }, + "x-go-name": "Failure" } } } @@ -2818,6 +3871,18 @@ func init() { "default": false, "name": "stats", "in": "query" + }, + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "name": "healthcheck", + "in": "query" } ], "responses": { @@ -2826,6 +3891,13 @@ func init() { "schema": { "$ref": "#/definitions/VirtualServerList" } + }, + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/VirtualServerList" + }, + "x-go-name": "NoContent" } } } @@ -2837,6 +3909,18 @@ func init() { ], "summary": "get a specific virtual server", "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "name": "healthcheck", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -2871,6 +3955,12 @@ func init() { ], "summary": "create or update virtual server", "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -3351,6 +4441,12 @@ func init() { "virtualserver" ], "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -3384,6 +4480,12 @@ func init() { "virtualserver" ], "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -3490,6 +4592,12 @@ func init() { "virtualserver" ], "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -3536,12 +4644,6 @@ func init() { "schema": { "$ref": "#/definitions/RealServerTinyList" } - }, - { - "type": "boolean", - "default": false, - "name": "healthcheck", - "in": "query" } ], "responses": { @@ -3586,6 +4688,12 @@ func init() { ], "summary": "Update fully real server list to vip:port:proto", "parameters": [ + { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, { "type": "string", "name": "VipPort", @@ -3689,6 +4797,68 @@ func init() { } } } + }, + "/vs/{VipPort}/rs/health": { + "put": { + "tags": [ + "virtualserver" + ], + "summary": "dpvs healthcheck update rs weight", + "parameters": [ + { + "type": "string", + "name": "version", + "in": "query", + "required": true + }, + { + "type": "string", + "name": "VipPort", + "in": "path", + "required": true + }, + { + "name": "rss", + "in": "body", + "schema": { + "$ref": "#/definitions/RealServerTinyList" + } + } + ], + "responses": { + "200": { + "description": "Success" + }, + "270": { + "description": "the rss-config parameter is outdated, update nothing and return the latest rs info\n", + "schema": { + "$ref": "#/definitions/VirtualServerSpecExpand" + }, + "x-go-name": "Unexpected" + }, + "460": { + "description": "Invalid frontend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidFrontend" + }, + "461": { + "description": "Invalid backend in service configuration", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "InvalidBackend" + }, + "500": { + "description": "Service deletion failed", + "schema": { + "$ref": "#/definitions/Error" + }, + "x-go-name": "Failure" + } + } + } } }, "definitions": { @@ -3718,6 +4888,9 @@ func init() { "properties": { "addr": { "type": "string" + }, + "ipset": { + "type": "string" } } }, @@ -3727,9 +4900,22 @@ func init() { "passive", "tcp", "udp", - "ping" + "ping", + "udpping", + "http" ] }, + "DpvsNodeSpec": { + "type": "object", + "properties": { + "AnnouncePort": { + "$ref": "#/definitions/VsAnnouncePort" + }, + "Laddrs": { + "$ref": "#/definitions/LocalAddressExpandList" + } + } + }, "Error": { "type": "string" }, @@ -3746,6 +4932,171 @@ func init() { } } }, + "IpsetCell": { + "description": "IpsetCell represents an indivisible granularity of ipset member.", + "type": "object", + "required": [ + "Type", + "Member" + ], + "properties": { + "Member": { + "$ref": "#/definitions/IpsetMember" + }, + "Type": { + "$ref": "#/definitions/IpsetType" + } + } + }, + "IpsetCreationOption": { + "description": "IpsetCreationOption contains all available options required in creating an ipset.\n", + "properties": { + "Comment": { + "type": "boolean", + "default": false + }, + "Family": { + "type": "string", + "enum": [ + "ipv4", + "ipv6" + ] + }, + "HashMaxElem": { + "type": "integer", + "format": "uint32" + }, + "HashSize": { + "type": "integer", + "format": "uint32" + }, + "Range": { + "description": "vaild format: ipv4-ipv4, ipv4/pfx, ipv6/pfx, port-port\n", + "type": "string" + } + } + }, + "IpsetInfo": { + "description": "IpsetInfo contains all parameters and information for ipset operations.\n", + "type": "object", + "required": [ + "Type", + "Name" + ], + "properties": { + "CreationOptions": { + "$ref": "#/definitions/IpsetCreationOption" + }, + "Entries": { + "type": "array", + "items": { + "$ref": "#/definitions/IpsetMember" + } + }, + "Name": { + "type": "string", + "maxLength": 32, + "minLength": 1 + }, + "Opcode": { + "description": "opertaion type code", + "type": "integer", + "format": "uint16", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "x-enum-varnames": [ + "Add", + "Del", + "Test", + "Create", + "Destroy", + "Flush", + "List" + ] + }, + "Type": { + "type": "string", + "$ref": "#/definitions/IpsetType" + } + } + }, + "IpsetInfoArray": { + "description": "IpsetInfoArray contains an array of ipset.", + "type": "object", + "properties": { + "Count": { + "type": "integer", + "format": "int32" + }, + "Infos": { + "type": "array", + "items": { + "$ref": "#/definitions/IpsetInfo" + } + } + } + }, + "IpsetMember": { + "description": "IpsetMember represents a specific entry in ipset.", + "type": "object", + "required": [ + "Entry" + ], + "properties": { + "Comment": { + "type": "string", + "maxLength": 32, + "minLength": 1 + }, + "Entry": { + "description": "type specific entry data, for example\n* 192.168.1.0/29 (bitmap:ip)\n* 192.168.88.0/24,tcp:8080-8082 (hash:net)\n* 2001::1,8080-8082,2002::aaaa:bbbb:ccc0:0/108 (hash:ip,port,net)\n", + "type": "string" + }, + "Options": { + "$ref": "#/definitions/IpsetOption" + } + } + }, + "IpsetOption": { + "description": "IpsetOption defines common options for ipset operations.", + "type": "object", + "properties": { + "Force": { + "description": "When add members to ipset with Force set, the already existing members are replaced sliently instead of emitting an EDPVS_EXIST error; When delete non-existent memebers from ipset, the DPSVS_NOTEXIST error is ignored.\n", + "type": "boolean", + "default": false + }, + "NoMatch": { + "description": "Nomatch excludes a small element range from an ipset, which is mainly used by network-cidr based ipset.\n", + "type": "boolean", + "default": false + } + } + }, + "IpsetType": { + "type": "string", + "enum": [ + "bitmap:ip", + "bitmap:ip,mac", + "bitmap:port", + "hash:ip", + "hash:net", + "hash:ip,port", + "hash:net,port", + "hash:net,port,iface", + "hash:ip,port,ip", + "hash:ip,port,net", + "hash:net,port,net", + "hash:net,port,net,port" + ] + }, "LocalAddressExpandList": { "properties": { "Items": { @@ -3981,6 +5332,17 @@ func init() { } } }, + "NodeServiceSnapshot": { + "type": "object", + "properties": { + "NodeSpec": { + "$ref": "#/definitions/DpvsNodeSpec" + }, + "Services": { + "$ref": "#/definitions/VirtualServerList" + } + } + }, "RealServerExpandList": { "type": "object", "properties": { @@ -4225,6 +5587,13 @@ func init() { 18 ] }, + "Quic": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, "RSs": { "$ref": "#/definitions/RealServerExpandList" }, @@ -4250,6 +5619,9 @@ func init() { "Timeout": { "type": "integer", "format": "uint32" + }, + "Version": { + "type": "string" } } }, @@ -4289,6 +5661,10 @@ func init() { "disable" ] }, + "Quic": { + "type": "boolean", + "default": false + }, "SchedName": { "type": "string", "enum": [ @@ -4320,6 +5696,17 @@ func init() { "type": "string" } } + }, + "VsAnnouncePort": { + "type": "object", + "properties": { + "dpvs": { + "type": "string" + }, + "switch": { + "type": "string" + } + } } }, "parameters": { @@ -4360,6 +5747,26 @@ func init() { "name": "healthcheck", "in": "query" }, + "ipset-cell": { + "name": "ipsetCell", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetCell" + } + }, + "ipset-name": { + "type": "string", + "name": "name", + "in": "path", + "required": true + }, + "ipset-param": { + "name": "ipsetParam", + "in": "body", + "schema": { + "$ref": "#/definitions/IpsetInfo" + } + }, "laddr-config": { "name": "spec", "in": "body", @@ -4428,6 +5835,12 @@ func init() { "in": "path", "required": true }, + "snapshot": { + "type": "boolean", + "default": true, + "name": "snapshot", + "in": "query" + }, "stats": { "type": "boolean", "default": false, @@ -4440,6 +5853,12 @@ func init() { "name": "verbose", "in": "query" }, + "version": { + "type": "string", + "name": "version", + "in": "query", + "required": true + }, "vlan-config": { "name": "spec", "in": "body", @@ -4487,6 +5906,10 @@ func init() { { "description": "arp", "name": "arp" + }, + { + "description": "ipset", + "name": "ipset" } ] }`)) diff --git a/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_parameters.go b/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_parameters.go index 07bc411bb..68e2a817b 100644 --- a/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_parameters.go +++ b/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_parameters.go @@ -25,11 +25,14 @@ func NewPutDeviceNameAddrParams() PutDeviceNameAddrParams { var ( // initialize parameters with default values - sapoolDefault = bool(false) + sapoolDefault = bool(false) + snapshotDefault = bool(true) ) return PutDeviceNameAddrParams{ Sapool: &sapoolDefault, + + Snapshot: &snapshotDefault, } } @@ -52,6 +55,11 @@ type PutDeviceNameAddrParams struct { Default: false */ Sapool *bool + /* + In: query + Default: true + */ + Snapshot *bool /* In: body */ @@ -79,6 +87,11 @@ func (o *PutDeviceNameAddrParams) BindRequest(r *http.Request, route *middleware res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + if runtime.HasBody(r) { defer r.Body.Close() var body models.InetAddrSpec @@ -143,3 +156,27 @@ func (o *PutDeviceNameAddrParams) bindSapool(rawData []string, hasKey bool, form return nil } + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *PutDeviceNameAddrParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewPutDeviceNameAddrParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_urlbuilder.go b/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_urlbuilder.go index a136e39c6..5374db59c 100644 --- a/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/device/put_device_name_addr_urlbuilder.go @@ -18,7 +18,8 @@ import ( type PutDeviceNameAddrURL struct { Name string - Sapool *bool + Sapool *bool + Snapshot *bool _basePath string // avoid unkeyed usage @@ -69,6 +70,14 @@ func (o *PutDeviceNameAddrURL) Build() (*url.URL, error) { qs.Set("sapool", sapoolQ) } + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + _result.RawQuery = qs.Encode() return &_result, nil diff --git a/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_parameters.go b/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_parameters.go index 7fcd77b11..4aceb7bec 100644 --- a/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_parameters.go +++ b/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_parameters.go @@ -12,17 +12,25 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" "github.com/go-openapi/validate" "github.com/dpvs-agent/models" ) // NewPutDeviceNameNetlinkAddrParams creates a new PutDeviceNameNetlinkAddrParams object -// -// There are no default values defined in the spec. +// with the default values initialized. func NewPutDeviceNameNetlinkAddrParams() PutDeviceNameNetlinkAddrParams { - return PutDeviceNameNetlinkAddrParams{} + var ( + // initialize parameters with default values + + snapshotDefault = bool(true) + ) + + return PutDeviceNameNetlinkAddrParams{ + Snapshot: &snapshotDefault, + } } // PutDeviceNameNetlinkAddrParams contains all the bound params for the put device name netlink addr operation @@ -39,6 +47,11 @@ type PutDeviceNameNetlinkAddrParams struct { In: path */ Name string + /* + In: query + Default: true + */ + Snapshot *bool /* In: body */ @@ -54,11 +67,18 @@ func (o *PutDeviceNameNetlinkAddrParams) BindRequest(r *http.Request, route *mid o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + rName, rhkName, _ := route.Params.GetOK("name") if err := o.bindName(rName, rhkName, route.Formats); err != nil { res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + if runtime.HasBody(r) { defer r.Body.Close() var body models.InetAddrSpec @@ -99,3 +119,27 @@ func (o *PutDeviceNameNetlinkAddrParams) bindName(rawData []string, hasKey bool, return nil } + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *PutDeviceNameNetlinkAddrParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewPutDeviceNameNetlinkAddrParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_urlbuilder.go b/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_urlbuilder.go index ef47a83b2..e47ed59b4 100644 --- a/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/device/put_device_name_netlink_addr_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // PutDeviceNameNetlinkAddrURL generates an URL for the put device name netlink addr operation type PutDeviceNameNetlinkAddrURL struct { Name string + Snapshot *bool + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *PutDeviceNameNetlinkAddrURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/tools/dpvs-agent/restapi/operations/dpvs_agent_api.go b/tools/dpvs-agent/restapi/operations/dpvs_agent_api.go index 5421e4aa7..ec6c9bf3c 100644 --- a/tools/dpvs-agent/restapi/operations/dpvs_agent_api.go +++ b/tools/dpvs-agent/restapi/operations/dpvs_agent_api.go @@ -20,6 +20,7 @@ import ( "github.com/go-openapi/swag" "github.com/dpvs-agent/restapi/operations/device" + "github.com/dpvs-agent/restapi/operations/ipset" "github.com/dpvs-agent/restapi/operations/virtualserver" ) @@ -45,6 +46,15 @@ func NewDpvsAgentAPI(spec *loads.Document) *DpvsAgentAPI { JSONProducer: runtime.JSONProducer(), + IpsetAddMemberHandler: ipset.AddMemberHandlerFunc(func(params ipset.AddMemberParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.AddMember has not yet been implemented") + }), + IpsetCreateHandler: ipset.CreateHandlerFunc(func(params ipset.CreateParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.Create has not yet been implemented") + }), + IpsetDelMemberHandler: ipset.DelMemberHandlerFunc(func(params ipset.DelMemberParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.DelMember has not yet been implemented") + }), DeviceDeleteDeviceNameAddrHandler: device.DeleteDeviceNameAddrHandlerFunc(func(params device.DeleteDeviceNameAddrParams) middleware.Responder { return middleware.NotImplemented("operation device.DeleteDeviceNameAddr has not yet been implemented") }), @@ -75,6 +85,15 @@ func NewDpvsAgentAPI(spec *loads.Document) *DpvsAgentAPI { VirtualserverDeleteVsVipPortRsHandler: virtualserver.DeleteVsVipPortRsHandlerFunc(func(params virtualserver.DeleteVsVipPortRsParams) middleware.Responder { return middleware.NotImplemented("operation virtualserver.DeleteVsVipPortRs has not yet been implemented") }), + IpsetDestroyHandler: ipset.DestroyHandlerFunc(func(params ipset.DestroyParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.Destroy has not yet been implemented") + }), + IpsetGetHandler: ipset.GetHandlerFunc(func(params ipset.GetParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.Get has not yet been implemented") + }), + IpsetGetAllHandler: ipset.GetAllHandlerFunc(func(params ipset.GetAllParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.GetAll has not yet been implemented") + }), DeviceGetDeviceHandler: device.GetDeviceHandlerFunc(func(params device.GetDeviceParams) middleware.Responder { return middleware.NotImplemented("operation device.GetDevice has not yet been implemented") }), @@ -114,6 +133,9 @@ func NewDpvsAgentAPI(spec *loads.Document) *DpvsAgentAPI { VirtualserverGetVsVipPortRsHandler: virtualserver.GetVsVipPortRsHandlerFunc(func(params virtualserver.GetVsVipPortRsParams) middleware.Responder { return middleware.NotImplemented("operation virtualserver.GetVsVipPortRs has not yet been implemented") }), + IpsetIsInHandler: ipset.IsInHandlerFunc(func(params ipset.IsInParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.IsIn has not yet been implemented") + }), VirtualserverPostVsVipPortAllowHandler: virtualserver.PostVsVipPortAllowHandlerFunc(func(params virtualserver.PostVsVipPortAllowParams) middleware.Responder { return middleware.NotImplemented("operation virtualserver.PostVsVipPortAllow has not yet been implemented") }), @@ -156,6 +178,12 @@ func NewDpvsAgentAPI(spec *loads.Document) *DpvsAgentAPI { VirtualserverPutVsVipPortRsHandler: virtualserver.PutVsVipPortRsHandlerFunc(func(params virtualserver.PutVsVipPortRsParams) middleware.Responder { return middleware.NotImplemented("operation virtualserver.PutVsVipPortRs has not yet been implemented") }), + VirtualserverPutVsVipPortRsHealthHandler: virtualserver.PutVsVipPortRsHealthHandlerFunc(func(params virtualserver.PutVsVipPortRsHealthParams) middleware.Responder { + return middleware.NotImplemented("operation virtualserver.PutVsVipPortRsHealth has not yet been implemented") + }), + IpsetReplaceMemberHandler: ipset.ReplaceMemberHandlerFunc(func(params ipset.ReplaceMemberParams) middleware.Responder { + return middleware.NotImplemented("operation ipset.ReplaceMember has not yet been implemented") + }), } } @@ -192,6 +220,12 @@ type DpvsAgentAPI struct { // - application/json JSONProducer runtime.Producer + // IpsetAddMemberHandler sets the operation handler for the add member operation + IpsetAddMemberHandler ipset.AddMemberHandler + // IpsetCreateHandler sets the operation handler for the create operation + IpsetCreateHandler ipset.CreateHandler + // IpsetDelMemberHandler sets the operation handler for the del member operation + IpsetDelMemberHandler ipset.DelMemberHandler // DeviceDeleteDeviceNameAddrHandler sets the operation handler for the delete device name addr operation DeviceDeleteDeviceNameAddrHandler device.DeleteDeviceNameAddrHandler // DeviceDeleteDeviceNameNetlinkHandler sets the operation handler for the delete device name netlink operation @@ -212,6 +246,12 @@ type DpvsAgentAPI struct { VirtualserverDeleteVsVipPortLaddrHandler virtualserver.DeleteVsVipPortLaddrHandler // VirtualserverDeleteVsVipPortRsHandler sets the operation handler for the delete vs vip port rs operation VirtualserverDeleteVsVipPortRsHandler virtualserver.DeleteVsVipPortRsHandler + // IpsetDestroyHandler sets the operation handler for the destroy operation + IpsetDestroyHandler ipset.DestroyHandler + // IpsetGetHandler sets the operation handler for the get operation + IpsetGetHandler ipset.GetHandler + // IpsetGetAllHandler sets the operation handler for the get all operation + IpsetGetAllHandler ipset.GetAllHandler // DeviceGetDeviceHandler sets the operation handler for the get device operation DeviceGetDeviceHandler device.GetDeviceHandler // DeviceGetDeviceNameAddrHandler sets the operation handler for the get device name addr operation @@ -238,6 +278,8 @@ type DpvsAgentAPI struct { VirtualserverGetVsVipPortLaddrHandler virtualserver.GetVsVipPortLaddrHandler // VirtualserverGetVsVipPortRsHandler sets the operation handler for the get vs vip port rs operation VirtualserverGetVsVipPortRsHandler virtualserver.GetVsVipPortRsHandler + // IpsetIsInHandler sets the operation handler for the is in operation + IpsetIsInHandler ipset.IsInHandler // VirtualserverPostVsVipPortAllowHandler sets the operation handler for the post vs vip port allow operation VirtualserverPostVsVipPortAllowHandler virtualserver.PostVsVipPortAllowHandler // VirtualserverPostVsVipPortDenyHandler sets the operation handler for the post vs vip port deny operation @@ -266,6 +308,10 @@ type DpvsAgentAPI struct { VirtualserverPutVsVipPortLaddrHandler virtualserver.PutVsVipPortLaddrHandler // VirtualserverPutVsVipPortRsHandler sets the operation handler for the put vs vip port rs operation VirtualserverPutVsVipPortRsHandler virtualserver.PutVsVipPortRsHandler + // VirtualserverPutVsVipPortRsHealthHandler sets the operation handler for the put vs vip port rs health operation + VirtualserverPutVsVipPortRsHealthHandler virtualserver.PutVsVipPortRsHealthHandler + // IpsetReplaceMemberHandler sets the operation handler for the replace member operation + IpsetReplaceMemberHandler ipset.ReplaceMemberHandler // ServeError is called when an error is received, there is a default handler // but you can set your own with this @@ -343,6 +389,15 @@ func (o *DpvsAgentAPI) Validate() error { unregistered = append(unregistered, "JSONProducer") } + if o.IpsetAddMemberHandler == nil { + unregistered = append(unregistered, "ipset.AddMemberHandler") + } + if o.IpsetCreateHandler == nil { + unregistered = append(unregistered, "ipset.CreateHandler") + } + if o.IpsetDelMemberHandler == nil { + unregistered = append(unregistered, "ipset.DelMemberHandler") + } if o.DeviceDeleteDeviceNameAddrHandler == nil { unregistered = append(unregistered, "device.DeleteDeviceNameAddrHandler") } @@ -373,6 +428,15 @@ func (o *DpvsAgentAPI) Validate() error { if o.VirtualserverDeleteVsVipPortRsHandler == nil { unregistered = append(unregistered, "virtualserver.DeleteVsVipPortRsHandler") } + if o.IpsetDestroyHandler == nil { + unregistered = append(unregistered, "ipset.DestroyHandler") + } + if o.IpsetGetHandler == nil { + unregistered = append(unregistered, "ipset.GetHandler") + } + if o.IpsetGetAllHandler == nil { + unregistered = append(unregistered, "ipset.GetAllHandler") + } if o.DeviceGetDeviceHandler == nil { unregistered = append(unregistered, "device.GetDeviceHandler") } @@ -412,6 +476,9 @@ func (o *DpvsAgentAPI) Validate() error { if o.VirtualserverGetVsVipPortRsHandler == nil { unregistered = append(unregistered, "virtualserver.GetVsVipPortRsHandler") } + if o.IpsetIsInHandler == nil { + unregistered = append(unregistered, "ipset.IsInHandler") + } if o.VirtualserverPostVsVipPortAllowHandler == nil { unregistered = append(unregistered, "virtualserver.PostVsVipPortAllowHandler") } @@ -454,6 +521,12 @@ func (o *DpvsAgentAPI) Validate() error { if o.VirtualserverPutVsVipPortRsHandler == nil { unregistered = append(unregistered, "virtualserver.PutVsVipPortRsHandler") } + if o.VirtualserverPutVsVipPortRsHealthHandler == nil { + unregistered = append(unregistered, "virtualserver.PutVsVipPortRsHealthHandler") + } + if o.IpsetReplaceMemberHandler == nil { + unregistered = append(unregistered, "ipset.ReplaceMemberHandler") + } if len(unregistered) > 0 { return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", ")) @@ -542,6 +615,18 @@ func (o *DpvsAgentAPI) initHandlerCache() { o.handlers = make(map[string]map[string]http.Handler) } + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/ipset/{name}/member"] = ipset.NewAddMember(o.context, o.IpsetAddMemberHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } + o.handlers["PUT"]["/ipset/{name}"] = ipset.NewCreate(o.context, o.IpsetCreateHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } + o.handlers["DELETE"]["/ipset/{name}/member"] = ipset.NewDelMember(o.context, o.IpsetDelMemberHandler) if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } @@ -582,6 +667,18 @@ func (o *DpvsAgentAPI) initHandlerCache() { o.handlers["DELETE"] = make(map[string]http.Handler) } o.handlers["DELETE"]["/vs/{VipPort}/rs"] = virtualserver.NewDeleteVsVipPortRs(o.context, o.VirtualserverDeleteVsVipPortRsHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } + o.handlers["DELETE"]["/ipset/{name}"] = ipset.NewDestroy(o.context, o.IpsetDestroyHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/ipset/{name}"] = ipset.NewGet(o.context, o.IpsetGetHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/ipset"] = ipset.NewGetAll(o.context, o.IpsetGetAllHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } @@ -637,6 +734,10 @@ func (o *DpvsAgentAPI) initHandlerCache() { if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } + o.handlers["POST"]["/ipset/{name}/cell"] = ipset.NewIsIn(o.context, o.IpsetIsInHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } o.handlers["POST"]["/vs/{VipPort}/allow"] = virtualserver.NewPostVsVipPortAllow(o.context, o.VirtualserverPostVsVipPortAllowHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) @@ -690,6 +791,14 @@ func (o *DpvsAgentAPI) initHandlerCache() { o.handlers["PUT"] = make(map[string]http.Handler) } o.handlers["PUT"]["/vs/{VipPort}/rs"] = virtualserver.NewPutVsVipPortRs(o.context, o.VirtualserverPutVsVipPortRsHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } + o.handlers["PUT"]["/vs/{VipPort}/rs/health"] = virtualserver.NewPutVsVipPortRsHealth(o.context, o.VirtualserverPutVsVipPortRsHealthHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } + o.handlers["PUT"]["/ipset/{name}/member"] = ipset.NewReplaceMember(o.context, o.IpsetReplaceMemberHandler) } // Serve creates a http handler to serve the API over HTTP diff --git a/tools/dpvs-agent/restapi/operations/ipset/add_member.go b/tools/dpvs-agent/restapi/operations/ipset/add_member.go new file mode 100644 index 000000000..58948ac0f --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/add_member.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// AddMemberHandlerFunc turns a function with the right signature into a add member handler +type AddMemberHandlerFunc func(AddMemberParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn AddMemberHandlerFunc) Handle(params AddMemberParams) middleware.Responder { + return fn(params) +} + +// AddMemberHandler interface for that can handle valid add member params +type AddMemberHandler interface { + Handle(AddMemberParams) middleware.Responder +} + +// NewAddMember creates a new http.Handler for the add member operation +func NewAddMember(ctx *middleware.Context, handler AddMemberHandler) *AddMember { + return &AddMember{Context: ctx, Handler: handler} +} + +/* + AddMember swagger:route POST /ipset/{name}/member ipset addMember + +Add members to the ipset. +*/ +type AddMember struct { + Context *middleware.Context + Handler AddMemberHandler +} + +func (o *AddMember) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewAddMemberParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/add_member_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/add_member_parameters.go new file mode 100644 index 000000000..b2b76bc29 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/add_member_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewAddMemberParams creates a new AddMemberParams object +// +// There are no default values defined in the spec. +func NewAddMemberParams() AddMemberParams { + + return AddMemberParams{} +} + +// AddMemberParams contains all the bound params for the add member operation +// typically these are obtained from a http.Request +// +// swagger:parameters AddMember +type AddMemberParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + IpsetParam *models.IpsetInfo + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewAddMemberParams() beforehand. +func (o *AddMemberParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.IpsetInfo + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("ipsetParam", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.IpsetParam = &body + } + } + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *AddMemberParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/add_member_responses.go b/tools/dpvs-agent/restapi/operations/ipset/add_member_responses.go new file mode 100644 index 000000000..89aba02da --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/add_member_responses.go @@ -0,0 +1,227 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// AddMemberOKCode is the HTTP code returned for type AddMemberOK +const AddMemberOKCode int = 200 + +/* +AddMemberOK Existed + +swagger:response addMemberOK +*/ +type AddMemberOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewAddMemberOK creates AddMemberOK with default headers values +func NewAddMemberOK() *AddMemberOK { + + return &AddMemberOK{} +} + +// WithPayload adds the payload to the add member o k response +func (o *AddMemberOK) WithPayload(payload string) *AddMemberOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the add member o k response +func (o *AddMemberOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *AddMemberOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// AddMemberCreatedCode is the HTTP code returned for type AddMemberCreated +const AddMemberCreatedCode int = 201 + +/* +AddMemberCreated Created + +swagger:response addMemberCreated +*/ +type AddMemberCreated struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewAddMemberCreated creates AddMemberCreated with default headers values +func NewAddMemberCreated() *AddMemberCreated { + + return &AddMemberCreated{} +} + +// WithPayload adds the payload to the add member created response +func (o *AddMemberCreated) WithPayload(payload string) *AddMemberCreated { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the add member created response +func (o *AddMemberCreated) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *AddMemberCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(201) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// AddMemberBadRequestCode is the HTTP code returned for type AddMemberBadRequest +const AddMemberBadRequestCode int = 400 + +/* +AddMemberBadRequest Invalid ipset parameter + +swagger:response addMemberBadRequest +*/ +type AddMemberBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewAddMemberBadRequest creates AddMemberBadRequest with default headers values +func NewAddMemberBadRequest() *AddMemberBadRequest { + + return &AddMemberBadRequest{} +} + +// WithPayload adds the payload to the add member bad request response +func (o *AddMemberBadRequest) WithPayload(payload string) *AddMemberBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the add member bad request response +func (o *AddMemberBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *AddMemberBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// AddMemberNotFoundCode is the HTTP code returned for type AddMemberNotFound +const AddMemberNotFoundCode int = 404 + +/* +AddMemberNotFound Ipset not found + +swagger:response addMemberNotFound +*/ +type AddMemberNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewAddMemberNotFound creates AddMemberNotFound with default headers values +func NewAddMemberNotFound() *AddMemberNotFound { + + return &AddMemberNotFound{} +} + +// WithPayload adds the payload to the add member not found response +func (o *AddMemberNotFound) WithPayload(payload string) *AddMemberNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the add member not found response +func (o *AddMemberNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *AddMemberNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// AddMemberFailureCode is the HTTP code returned for type AddMemberFailure +const AddMemberFailureCode int = 500 + +/* +AddMemberFailure Service not available + +swagger:response addMemberFailure +*/ +type AddMemberFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewAddMemberFailure creates AddMemberFailure with default headers values +func NewAddMemberFailure() *AddMemberFailure { + + return &AddMemberFailure{} +} + +// WithPayload adds the payload to the add member failure response +func (o *AddMemberFailure) WithPayload(payload string) *AddMemberFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the add member failure response +func (o *AddMemberFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *AddMemberFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/add_member_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/add_member_urlbuilder.go new file mode 100644 index 000000000..25bc040c4 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/add_member_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// AddMemberURL generates an URL for the add member operation +type AddMemberURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *AddMemberURL) WithBasePath(bp string) *AddMemberURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *AddMemberURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *AddMemberURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}/member" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on AddMemberURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *AddMemberURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *AddMemberURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *AddMemberURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on AddMemberURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on AddMemberURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *AddMemberURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/create.go b/tools/dpvs-agent/restapi/operations/ipset/create.go new file mode 100644 index 000000000..211fa29e0 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/create.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// CreateHandlerFunc turns a function with the right signature into a create handler +type CreateHandlerFunc func(CreateParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn CreateHandlerFunc) Handle(params CreateParams) middleware.Responder { + return fn(params) +} + +// CreateHandler interface for that can handle valid create params +type CreateHandler interface { + Handle(CreateParams) middleware.Responder +} + +// NewCreate creates a new http.Handler for the create operation +func NewCreate(ctx *middleware.Context, handler CreateHandler) *Create { + return &Create{Context: ctx, Handler: handler} +} + +/* + Create swagger:route PUT /ipset/{name} ipset create + +Create an ipset named {name}. +*/ +type Create struct { + Context *middleware.Context + Handler CreateHandler +} + +func (o *Create) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewCreateParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/create_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/create_parameters.go new file mode 100644 index 000000000..7aca005bf --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/create_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewCreateParams creates a new CreateParams object +// +// There are no default values defined in the spec. +func NewCreateParams() CreateParams { + + return CreateParams{} +} + +// CreateParams contains all the bound params for the create operation +// typically these are obtained from a http.Request +// +// swagger:parameters Create +type CreateParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + IpsetParam *models.IpsetInfo + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewCreateParams() beforehand. +func (o *CreateParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.IpsetInfo + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("ipsetParam", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.IpsetParam = &body + } + } + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *CreateParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/create_responses.go b/tools/dpvs-agent/restapi/operations/ipset/create_responses.go new file mode 100644 index 000000000..7bd038576 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/create_responses.go @@ -0,0 +1,227 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// CreateOKCode is the HTTP code returned for type CreateOK +const CreateOKCode int = 200 + +/* +CreateOK Replaced + +swagger:response createOK +*/ +type CreateOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewCreateOK creates CreateOK with default headers values +func NewCreateOK() *CreateOK { + + return &CreateOK{} +} + +// WithPayload adds the payload to the create o k response +func (o *CreateOK) WithPayload(payload string) *CreateOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create o k response +func (o *CreateOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// CreateCreatedCode is the HTTP code returned for type CreateCreated +const CreateCreatedCode int = 201 + +/* +CreateCreated Created + +swagger:response createCreated +*/ +type CreateCreated struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewCreateCreated creates CreateCreated with default headers values +func NewCreateCreated() *CreateCreated { + + return &CreateCreated{} +} + +// WithPayload adds the payload to the create created response +func (o *CreateCreated) WithPayload(payload string) *CreateCreated { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create created response +func (o *CreateCreated) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(201) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// CreateBadRequestCode is the HTTP code returned for type CreateBadRequest +const CreateBadRequestCode int = 400 + +/* +CreateBadRequest Invalid ipset parameter + +swagger:response createBadRequest +*/ +type CreateBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewCreateBadRequest creates CreateBadRequest with default headers values +func NewCreateBadRequest() *CreateBadRequest { + + return &CreateBadRequest{} +} + +// WithPayload adds the payload to the create bad request response +func (o *CreateBadRequest) WithPayload(payload string) *CreateBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create bad request response +func (o *CreateBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// CreateNotFoundCode is the HTTP code returned for type CreateNotFound +const CreateNotFoundCode int = 404 + +/* +CreateNotFound Ipset not found + +swagger:response createNotFound +*/ +type CreateNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewCreateNotFound creates CreateNotFound with default headers values +func NewCreateNotFound() *CreateNotFound { + + return &CreateNotFound{} +} + +// WithPayload adds the payload to the create not found response +func (o *CreateNotFound) WithPayload(payload string) *CreateNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create not found response +func (o *CreateNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// CreateFailureCode is the HTTP code returned for type CreateFailure +const CreateFailureCode int = 500 + +/* +CreateFailure Service not available + +swagger:response createFailure +*/ +type CreateFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewCreateFailure creates CreateFailure with default headers values +func NewCreateFailure() *CreateFailure { + + return &CreateFailure{} +} + +// WithPayload adds the payload to the create failure response +func (o *CreateFailure) WithPayload(payload string) *CreateFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create failure response +func (o *CreateFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/create_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/create_urlbuilder.go new file mode 100644 index 000000000..fe549f1ab --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/create_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// CreateURL generates an URL for the create operation +type CreateURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *CreateURL) WithBasePath(bp string) *CreateURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *CreateURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *CreateURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on CreateURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *CreateURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *CreateURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *CreateURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on CreateURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on CreateURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *CreateURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/del_member.go b/tools/dpvs-agent/restapi/operations/ipset/del_member.go new file mode 100644 index 000000000..c72e13706 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/del_member.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// DelMemberHandlerFunc turns a function with the right signature into a del member handler +type DelMemberHandlerFunc func(DelMemberParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn DelMemberHandlerFunc) Handle(params DelMemberParams) middleware.Responder { + return fn(params) +} + +// DelMemberHandler interface for that can handle valid del member params +type DelMemberHandler interface { + Handle(DelMemberParams) middleware.Responder +} + +// NewDelMember creates a new http.Handler for the del member operation +func NewDelMember(ctx *middleware.Context, handler DelMemberHandler) *DelMember { + return &DelMember{Context: ctx, Handler: handler} +} + +/* + DelMember swagger:route DELETE /ipset/{name}/member ipset delMember + +Delete members from the ipset. +*/ +type DelMember struct { + Context *middleware.Context + Handler DelMemberHandler +} + +func (o *DelMember) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewDelMemberParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/del_member_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/del_member_parameters.go new file mode 100644 index 000000000..58ca3b276 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/del_member_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewDelMemberParams creates a new DelMemberParams object +// +// There are no default values defined in the spec. +func NewDelMemberParams() DelMemberParams { + + return DelMemberParams{} +} + +// DelMemberParams contains all the bound params for the del member operation +// typically these are obtained from a http.Request +// +// swagger:parameters DelMember +type DelMemberParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + IpsetParam *models.IpsetInfo + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewDelMemberParams() beforehand. +func (o *DelMemberParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.IpsetInfo + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("ipsetParam", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.IpsetParam = &body + } + } + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *DelMemberParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/del_member_responses.go b/tools/dpvs-agent/restapi/operations/ipset/del_member_responses.go new file mode 100644 index 000000000..34767169d --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/del_member_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// DelMemberOKCode is the HTTP code returned for type DelMemberOK +const DelMemberOKCode int = 200 + +/* +DelMemberOK Succeed + +swagger:response delMemberOK +*/ +type DelMemberOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDelMemberOK creates DelMemberOK with default headers values +func NewDelMemberOK() *DelMemberOK { + + return &DelMemberOK{} +} + +// WithPayload adds the payload to the del member o k response +func (o *DelMemberOK) WithPayload(payload string) *DelMemberOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the del member o k response +func (o *DelMemberOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DelMemberOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// DelMemberBadRequestCode is the HTTP code returned for type DelMemberBadRequest +const DelMemberBadRequestCode int = 400 + +/* +DelMemberBadRequest Invalid ipset parameter + +swagger:response delMemberBadRequest +*/ +type DelMemberBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDelMemberBadRequest creates DelMemberBadRequest with default headers values +func NewDelMemberBadRequest() *DelMemberBadRequest { + + return &DelMemberBadRequest{} +} + +// WithPayload adds the payload to the del member bad request response +func (o *DelMemberBadRequest) WithPayload(payload string) *DelMemberBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the del member bad request response +func (o *DelMemberBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DelMemberBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// DelMemberNotFoundCode is the HTTP code returned for type DelMemberNotFound +const DelMemberNotFoundCode int = 404 + +/* +DelMemberNotFound Ipset not found + +swagger:response delMemberNotFound +*/ +type DelMemberNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDelMemberNotFound creates DelMemberNotFound with default headers values +func NewDelMemberNotFound() *DelMemberNotFound { + + return &DelMemberNotFound{} +} + +// WithPayload adds the payload to the del member not found response +func (o *DelMemberNotFound) WithPayload(payload string) *DelMemberNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the del member not found response +func (o *DelMemberNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DelMemberNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// DelMemberFailureCode is the HTTP code returned for type DelMemberFailure +const DelMemberFailureCode int = 500 + +/* +DelMemberFailure Service not available + +swagger:response delMemberFailure +*/ +type DelMemberFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDelMemberFailure creates DelMemberFailure with default headers values +func NewDelMemberFailure() *DelMemberFailure { + + return &DelMemberFailure{} +} + +// WithPayload adds the payload to the del member failure response +func (o *DelMemberFailure) WithPayload(payload string) *DelMemberFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the del member failure response +func (o *DelMemberFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DelMemberFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/del_member_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/del_member_urlbuilder.go new file mode 100644 index 000000000..8da232e0b --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/del_member_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// DelMemberURL generates an URL for the del member operation +type DelMemberURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DelMemberURL) WithBasePath(bp string) *DelMemberURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DelMemberURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *DelMemberURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}/member" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on DelMemberURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *DelMemberURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *DelMemberURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *DelMemberURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on DelMemberURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on DelMemberURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *DelMemberURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/destroy.go b/tools/dpvs-agent/restapi/operations/ipset/destroy.go new file mode 100644 index 000000000..6f8c38afd --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/destroy.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// DestroyHandlerFunc turns a function with the right signature into a destroy handler +type DestroyHandlerFunc func(DestroyParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn DestroyHandlerFunc) Handle(params DestroyParams) middleware.Responder { + return fn(params) +} + +// DestroyHandler interface for that can handle valid destroy params +type DestroyHandler interface { + Handle(DestroyParams) middleware.Responder +} + +// NewDestroy creates a new http.Handler for the destroy operation +func NewDestroy(ctx *middleware.Context, handler DestroyHandler) *Destroy { + return &Destroy{Context: ctx, Handler: handler} +} + +/* + Destroy swagger:route DELETE /ipset/{name} ipset destroy + +Delete the ipset named {name}. +*/ +type Destroy struct { + Context *middleware.Context + Handler DestroyHandler +} + +func (o *Destroy) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewDestroyParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/destroy_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/destroy_parameters.go new file mode 100644 index 000000000..738d1688f --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/destroy_parameters.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewDestroyParams creates a new DestroyParams object +// +// There are no default values defined in the spec. +func NewDestroyParams() DestroyParams { + + return DestroyParams{} +} + +// DestroyParams contains all the bound params for the destroy operation +// typically these are obtained from a http.Request +// +// swagger:parameters Destroy +type DestroyParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewDestroyParams() beforehand. +func (o *DestroyParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *DestroyParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/destroy_responses.go b/tools/dpvs-agent/restapi/operations/ipset/destroy_responses.go new file mode 100644 index 000000000..4499b32c6 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/destroy_responses.go @@ -0,0 +1,141 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// DestroyOKCode is the HTTP code returned for type DestroyOK +const DestroyOKCode int = 200 + +/* +DestroyOK Deleted + +swagger:response destroyOK +*/ +type DestroyOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDestroyOK creates DestroyOK with default headers values +func NewDestroyOK() *DestroyOK { + + return &DestroyOK{} +} + +// WithPayload adds the payload to the destroy o k response +func (o *DestroyOK) WithPayload(payload string) *DestroyOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the destroy o k response +func (o *DestroyOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DestroyOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// DestroyNotFoundCode is the HTTP code returned for type DestroyNotFound +const DestroyNotFoundCode int = 404 + +/* +DestroyNotFound Ipset not found + +swagger:response destroyNotFound +*/ +type DestroyNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDestroyNotFound creates DestroyNotFound with default headers values +func NewDestroyNotFound() *DestroyNotFound { + + return &DestroyNotFound{} +} + +// WithPayload adds the payload to the destroy not found response +func (o *DestroyNotFound) WithPayload(payload string) *DestroyNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the destroy not found response +func (o *DestroyNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DestroyNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// DestroyFailureCode is the HTTP code returned for type DestroyFailure +const DestroyFailureCode int = 500 + +/* +DestroyFailure Service not available + +swagger:response destroyFailure +*/ +type DestroyFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewDestroyFailure creates DestroyFailure with default headers values +func NewDestroyFailure() *DestroyFailure { + + return &DestroyFailure{} +} + +// WithPayload adds the payload to the destroy failure response +func (o *DestroyFailure) WithPayload(payload string) *DestroyFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the destroy failure response +func (o *DestroyFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *DestroyFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/destroy_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/destroy_urlbuilder.go new file mode 100644 index 000000000..fd259b65f --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/destroy_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// DestroyURL generates an URL for the destroy operation +type DestroyURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DestroyURL) WithBasePath(bp string) *DestroyURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *DestroyURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *DestroyURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on DestroyURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *DestroyURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *DestroyURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *DestroyURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on DestroyURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on DestroyURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *DestroyURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get.go b/tools/dpvs-agent/restapi/operations/ipset/get.go new file mode 100644 index 000000000..35d31e50e --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetHandlerFunc turns a function with the right signature into a get handler +type GetHandlerFunc func(GetParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetHandlerFunc) Handle(params GetParams) middleware.Responder { + return fn(params) +} + +// GetHandler interface for that can handle valid get params +type GetHandler interface { + Handle(GetParams) middleware.Responder +} + +// NewGet creates a new http.Handler for the get operation +func NewGet(ctx *middleware.Context, handler GetHandler) *Get { + return &Get{Context: ctx, Handler: handler} +} + +/* + Get swagger:route GET /ipset/{name} ipset get + +Get a specific ipset and its members. +*/ +type Get struct { + Context *middleware.Context + Handler GetHandler +} + +func (o *Get) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_all.go b/tools/dpvs-agent/restapi/operations/ipset/get_all.go new file mode 100644 index 000000000..d5a981a4a --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_all.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetAllHandlerFunc turns a function with the right signature into a get all handler +type GetAllHandlerFunc func(GetAllParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetAllHandlerFunc) Handle(params GetAllParams) middleware.Responder { + return fn(params) +} + +// GetAllHandler interface for that can handle valid get all params +type GetAllHandler interface { + Handle(GetAllParams) middleware.Responder +} + +// NewGetAll creates a new http.Handler for the get all operation +func NewGetAll(ctx *middleware.Context, handler GetAllHandler) *GetAll { + return &GetAll{Context: ctx, Handler: handler} +} + +/* + GetAll swagger:route GET /ipset ipset getAll + +Get all the ipsets and members. +*/ +type GetAll struct { + Context *middleware.Context + Handler GetAllHandler +} + +func (o *GetAll) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetAllParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_all_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/get_all_parameters.go new file mode 100644 index 000000000..a92ff5454 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_all_parameters.go @@ -0,0 +1,46 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetAllParams creates a new GetAllParams object +// +// There are no default values defined in the spec. +func NewGetAllParams() GetAllParams { + + return GetAllParams{} +} + +// GetAllParams contains all the bound params for the get all operation +// typically these are obtained from a http.Request +// +// swagger:parameters GetAll +type GetAllParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetAllParams() beforehand. +func (o *GetAllParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_all_responses.go b/tools/dpvs-agent/restapi/operations/ipset/get_all_responses.go new file mode 100644 index 000000000..433af030f --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_all_responses.go @@ -0,0 +1,102 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/dpvs-agent/models" +) + +// GetAllOKCode is the HTTP code returned for type GetAllOK +const GetAllOKCode int = 200 + +/* +GetAllOK Success + +swagger:response getAllOK +*/ +type GetAllOK struct { + + /* + In: Body + */ + Payload *models.IpsetInfoArray `json:"body,omitempty"` +} + +// NewGetAllOK creates GetAllOK with default headers values +func NewGetAllOK() *GetAllOK { + + return &GetAllOK{} +} + +// WithPayload adds the payload to the get all o k response +func (o *GetAllOK) WithPayload(payload *models.IpsetInfoArray) *GetAllOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get all o k response +func (o *GetAllOK) SetPayload(payload *models.IpsetInfoArray) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAllOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetAllFailureCode is the HTTP code returned for type GetAllFailure +const GetAllFailureCode int = 500 + +/* +GetAllFailure Service not available + +swagger:response getAllFailure +*/ +type GetAllFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetAllFailure creates GetAllFailure with default headers values +func NewGetAllFailure() *GetAllFailure { + + return &GetAllFailure{} +} + +// WithPayload adds the payload to the get all failure response +func (o *GetAllFailure) WithPayload(payload string) *GetAllFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get all failure response +func (o *GetAllFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAllFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_all_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/get_all_urlbuilder.go new file mode 100644 index 000000000..2fd0ac230 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_all_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetAllURL generates an URL for the get all operation +type GetAllURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAllURL) WithBasePath(bp string) *GetAllURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAllURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetAllURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetAllURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetAllURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetAllURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetAllURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetAllURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetAllURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/get_parameters.go new file mode 100644 index 000000000..10a335867 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_parameters.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetParams creates a new GetParams object +// +// There are no default values defined in the spec. +func NewGetParams() GetParams { + + return GetParams{} +} + +// GetParams contains all the bound params for the get operation +// typically these are obtained from a http.Request +// +// swagger:parameters Get +type GetParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetParams() beforehand. +func (o *GetParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *GetParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_responses.go b/tools/dpvs-agent/restapi/operations/ipset/get_responses.go new file mode 100644 index 000000000..c2018050a --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_responses.go @@ -0,0 +1,145 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/dpvs-agent/models" +) + +// GetOKCode is the HTTP code returned for type GetOK +const GetOKCode int = 200 + +/* +GetOK Success + +swagger:response getOK +*/ +type GetOK struct { + + /* + In: Body + */ + Payload *models.IpsetInfo `json:"body,omitempty"` +} + +// NewGetOK creates GetOK with default headers values +func NewGetOK() *GetOK { + + return &GetOK{} +} + +// WithPayload adds the payload to the get o k response +func (o *GetOK) WithPayload(payload *models.IpsetInfo) *GetOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get o k response +func (o *GetOK) SetPayload(payload *models.IpsetInfo) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetNotFoundCode is the HTTP code returned for type GetNotFound +const GetNotFoundCode int = 404 + +/* +GetNotFound Ipset not found + +swagger:response getNotFound +*/ +type GetNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetNotFound creates GetNotFound with default headers values +func NewGetNotFound() *GetNotFound { + + return &GetNotFound{} +} + +// WithPayload adds the payload to the get not found response +func (o *GetNotFound) WithPayload(payload string) *GetNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get not found response +func (o *GetNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetFailureCode is the HTTP code returned for type GetFailure +const GetFailureCode int = 500 + +/* +GetFailure Service not available + +swagger:response getFailure +*/ +type GetFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewGetFailure creates GetFailure with default headers values +func NewGetFailure() *GetFailure { + + return &GetFailure{} +} + +// WithPayload adds the payload to the get failure response +func (o *GetFailure) WithPayload(payload string) *GetFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get failure response +func (o *GetFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/get_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/get_urlbuilder.go new file mode 100644 index 000000000..0eeff8b86 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/get_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetURL generates an URL for the get operation +type GetURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetURL) WithBasePath(bp string) *GetURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on GetURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/is_in.go b/tools/dpvs-agent/restapi/operations/ipset/is_in.go new file mode 100644 index 000000000..ef011b31a --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/is_in.go @@ -0,0 +1,120 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "context" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IsInHandlerFunc turns a function with the right signature into a is in handler +type IsInHandlerFunc func(IsInParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn IsInHandlerFunc) Handle(params IsInParams) middleware.Responder { + return fn(params) +} + +// IsInHandler interface for that can handle valid is in params +type IsInHandler interface { + Handle(IsInParams) middleware.Responder +} + +// NewIsIn creates a new http.Handler for the is in operation +func NewIsIn(ctx *middleware.Context, handler IsInHandler) *IsIn { + return &IsIn{Context: ctx, Handler: handler} +} + +/* + IsIn swagger:route POST /ipset/{name}/cell ipset isIn + +Check if an object in the ipset. +*/ +type IsIn struct { + Context *middleware.Context + Handler IsInHandler +} + +func (o *IsIn) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewIsInParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} + +// IsInOKBody is in o k body +// +// swagger:model IsInOKBody +type IsInOKBody struct { + + // message + Message string `json:"Message,omitempty"` + + // result + // Required: true + Result *bool `json:"Result"` +} + +// Validate validates this is in o k body +func (o *IsInOKBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateResult(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *IsInOKBody) validateResult(formats strfmt.Registry) error { + + if err := validate.Required("isInOK"+"."+"Result", "body", o.Result); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this is in o k body based on context it is used +func (o *IsInOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *IsInOKBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *IsInOKBody) UnmarshalBinary(b []byte) error { + var res IsInOKBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/is_in_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/is_in_parameters.go new file mode 100644 index 000000000..06236abce --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/is_in_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewIsInParams creates a new IsInParams object +// +// There are no default values defined in the spec. +func NewIsInParams() IsInParams { + + return IsInParams{} +} + +// IsInParams contains all the bound params for the is in operation +// typically these are obtained from a http.Request +// +// swagger:parameters IsIn +type IsInParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + IpsetCell *models.IpsetCell + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewIsInParams() beforehand. +func (o *IsInParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.IpsetCell + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("ipsetCell", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.IpsetCell = &body + } + } + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *IsInParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/is_in_responses.go b/tools/dpvs-agent/restapi/operations/ipset/is_in_responses.go new file mode 100644 index 000000000..4565ceabb --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/is_in_responses.go @@ -0,0 +1,186 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// IsInOKCode is the HTTP code returned for type IsInOK +const IsInOKCode int = 200 + +/* +IsInOK Succeed + +swagger:response isInOK +*/ +type IsInOK struct { + + /* + In: Body + */ + Payload *IsInOKBody `json:"body,omitempty"` +} + +// NewIsInOK creates IsInOK with default headers values +func NewIsInOK() *IsInOK { + + return &IsInOK{} +} + +// WithPayload adds the payload to the is in o k response +func (o *IsInOK) WithPayload(payload *IsInOKBody) *IsInOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the is in o k response +func (o *IsInOK) SetPayload(payload *IsInOKBody) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *IsInOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// IsInBadRequestCode is the HTTP code returned for type IsInBadRequest +const IsInBadRequestCode int = 400 + +/* +IsInBadRequest Invalid ipset parameter + +swagger:response isInBadRequest +*/ +type IsInBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewIsInBadRequest creates IsInBadRequest with default headers values +func NewIsInBadRequest() *IsInBadRequest { + + return &IsInBadRequest{} +} + +// WithPayload adds the payload to the is in bad request response +func (o *IsInBadRequest) WithPayload(payload string) *IsInBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the is in bad request response +func (o *IsInBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *IsInBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// IsInNotFoundCode is the HTTP code returned for type IsInNotFound +const IsInNotFoundCode int = 404 + +/* +IsInNotFound Ipset not found + +swagger:response isInNotFound +*/ +type IsInNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewIsInNotFound creates IsInNotFound with default headers values +func NewIsInNotFound() *IsInNotFound { + + return &IsInNotFound{} +} + +// WithPayload adds the payload to the is in not found response +func (o *IsInNotFound) WithPayload(payload string) *IsInNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the is in not found response +func (o *IsInNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *IsInNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// IsInFailureCode is the HTTP code returned for type IsInFailure +const IsInFailureCode int = 500 + +/* +IsInFailure Service not available + +swagger:response isInFailure +*/ +type IsInFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewIsInFailure creates IsInFailure with default headers values +func NewIsInFailure() *IsInFailure { + + return &IsInFailure{} +} + +// WithPayload adds the payload to the is in failure response +func (o *IsInFailure) WithPayload(payload string) *IsInFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the is in failure response +func (o *IsInFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *IsInFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/is_in_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/is_in_urlbuilder.go new file mode 100644 index 000000000..4565d4dda --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/is_in_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// IsInURL generates an URL for the is in operation +type IsInURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *IsInURL) WithBasePath(bp string) *IsInURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *IsInURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *IsInURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}/cell" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on IsInURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *IsInURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *IsInURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *IsInURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on IsInURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on IsInURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *IsInURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/replace_member.go b/tools/dpvs-agent/restapi/operations/ipset/replace_member.go new file mode 100644 index 000000000..09668e599 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/replace_member.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// ReplaceMemberHandlerFunc turns a function with the right signature into a replace member handler +type ReplaceMemberHandlerFunc func(ReplaceMemberParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn ReplaceMemberHandlerFunc) Handle(params ReplaceMemberParams) middleware.Responder { + return fn(params) +} + +// ReplaceMemberHandler interface for that can handle valid replace member params +type ReplaceMemberHandler interface { + Handle(ReplaceMemberParams) middleware.Responder +} + +// NewReplaceMember creates a new http.Handler for the replace member operation +func NewReplaceMember(ctx *middleware.Context, handler ReplaceMemberHandler) *ReplaceMember { + return &ReplaceMember{Context: ctx, Handler: handler} +} + +/* + ReplaceMember swagger:route PUT /ipset/{name}/member ipset replaceMember + +Reset the whole ipset members. +*/ +type ReplaceMember struct { + Context *middleware.Context + Handler ReplaceMemberHandler +} + +func (o *ReplaceMember) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewReplaceMemberParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/replace_member_parameters.go b/tools/dpvs-agent/restapi/operations/ipset/replace_member_parameters.go new file mode 100644 index 000000000..95c1cb0da --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/replace_member_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewReplaceMemberParams creates a new ReplaceMemberParams object +// +// There are no default values defined in the spec. +func NewReplaceMemberParams() ReplaceMemberParams { + + return ReplaceMemberParams{} +} + +// ReplaceMemberParams contains all the bound params for the replace member operation +// typically these are obtained from a http.Request +// +// swagger:parameters ReplaceMember +type ReplaceMemberParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + IpsetParam *models.IpsetInfo + /* + Required: true + In: path + */ + Name string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewReplaceMemberParams() beforehand. +func (o *ReplaceMemberParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.IpsetInfo + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("ipsetParam", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.IpsetParam = &body + } + } + } + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *ReplaceMemberParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Name = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/replace_member_responses.go b/tools/dpvs-agent/restapi/operations/ipset/replace_member_responses.go new file mode 100644 index 000000000..f402f2f43 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/replace_member_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// ReplaceMemberOKCode is the HTTP code returned for type ReplaceMemberOK +const ReplaceMemberOKCode int = 200 + +/* +ReplaceMemberOK Succeed + +swagger:response replaceMemberOK +*/ +type ReplaceMemberOK struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewReplaceMemberOK creates ReplaceMemberOK with default headers values +func NewReplaceMemberOK() *ReplaceMemberOK { + + return &ReplaceMemberOK{} +} + +// WithPayload adds the payload to the replace member o k response +func (o *ReplaceMemberOK) WithPayload(payload string) *ReplaceMemberOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the replace member o k response +func (o *ReplaceMemberOK) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ReplaceMemberOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// ReplaceMemberBadRequestCode is the HTTP code returned for type ReplaceMemberBadRequest +const ReplaceMemberBadRequestCode int = 400 + +/* +ReplaceMemberBadRequest Invalid ipset parameter + +swagger:response replaceMemberBadRequest +*/ +type ReplaceMemberBadRequest struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewReplaceMemberBadRequest creates ReplaceMemberBadRequest with default headers values +func NewReplaceMemberBadRequest() *ReplaceMemberBadRequest { + + return &ReplaceMemberBadRequest{} +} + +// WithPayload adds the payload to the replace member bad request response +func (o *ReplaceMemberBadRequest) WithPayload(payload string) *ReplaceMemberBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the replace member bad request response +func (o *ReplaceMemberBadRequest) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ReplaceMemberBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// ReplaceMemberNotFoundCode is the HTTP code returned for type ReplaceMemberNotFound +const ReplaceMemberNotFoundCode int = 404 + +/* +ReplaceMemberNotFound Ipset not found + +swagger:response replaceMemberNotFound +*/ +type ReplaceMemberNotFound struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewReplaceMemberNotFound creates ReplaceMemberNotFound with default headers values +func NewReplaceMemberNotFound() *ReplaceMemberNotFound { + + return &ReplaceMemberNotFound{} +} + +// WithPayload adds the payload to the replace member not found response +func (o *ReplaceMemberNotFound) WithPayload(payload string) *ReplaceMemberNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the replace member not found response +func (o *ReplaceMemberNotFound) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ReplaceMemberNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// ReplaceMemberFailureCode is the HTTP code returned for type ReplaceMemberFailure +const ReplaceMemberFailureCode int = 500 + +/* +ReplaceMemberFailure Service not available + +swagger:response replaceMemberFailure +*/ +type ReplaceMemberFailure struct { + + /* + In: Body + */ + Payload string `json:"body,omitempty"` +} + +// NewReplaceMemberFailure creates ReplaceMemberFailure with default headers values +func NewReplaceMemberFailure() *ReplaceMemberFailure { + + return &ReplaceMemberFailure{} +} + +// WithPayload adds the payload to the replace member failure response +func (o *ReplaceMemberFailure) WithPayload(payload string) *ReplaceMemberFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the replace member failure response +func (o *ReplaceMemberFailure) SetPayload(payload string) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ReplaceMemberFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/ipset/replace_member_urlbuilder.go b/tools/dpvs-agent/restapi/operations/ipset/replace_member_urlbuilder.go new file mode 100644 index 000000000..e49b05b76 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/ipset/replace_member_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ipset + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// ReplaceMemberURL generates an URL for the replace member operation +type ReplaceMemberURL struct { + Name string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ReplaceMemberURL) WithBasePath(bp string) *ReplaceMemberURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ReplaceMemberURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *ReplaceMemberURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/ipset/{name}/member" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on ReplaceMemberURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *ReplaceMemberURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *ReplaceMemberURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *ReplaceMemberURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on ReplaceMemberURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on ReplaceMemberURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *ReplaceMemberURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_parameters.go index ac28ba465..baac157f1 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_parameters.go @@ -22,10 +22,16 @@ func NewGetVsParams() GetVsParams { var ( // initialize parameters with default values - statsDefault = bool(false) + healthcheckDefault = bool(false) + snapshotDefault = bool(true) + statsDefault = bool(false) ) return GetVsParams{ + Healthcheck: &healthcheckDefault, + + Snapshot: &snapshotDefault, + Stats: &statsDefault, } } @@ -39,6 +45,16 @@ type GetVsParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /* + In: query + Default: false + */ + Healthcheck *bool + /* + In: query + Default: true + */ + Snapshot *bool /* In: query Default: false @@ -57,6 +73,16 @@ func (o *GetVsParams) BindRequest(r *http.Request, route *middleware.MatchedRout qs := runtime.Values(r.URL.Query()) + qHealthcheck, qhkHealthcheck, _ := qs.GetOK("healthcheck") + if err := o.bindHealthcheck(qHealthcheck, qhkHealthcheck, route.Formats); err != nil { + res = append(res, err) + } + + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + qStats, qhkStats, _ := qs.GetOK("stats") if err := o.bindStats(qStats, qhkStats, route.Formats); err != nil { res = append(res, err) @@ -67,6 +93,54 @@ func (o *GetVsParams) BindRequest(r *http.Request, route *middleware.MatchedRout return nil } +// bindHealthcheck binds and validates parameter Healthcheck from query. +func (o *GetVsParams) bindHealthcheck(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("healthcheck", "query", "bool", raw) + } + o.Healthcheck = &value + + return nil +} + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *GetVsParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} + // bindStats binds and validates parameter Stats from query. func (o *GetVsParams) bindStats(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_responses.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_responses.go index c67eec7a7..701d342e1 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_responses.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_responses.go @@ -57,3 +57,48 @@ func (o *GetVsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Produce } } } + +// GetVsNoContentCode is the HTTP code returned for type GetVsNoContent +const GetVsNoContentCode int = 204 + +/* +GetVsNoContent No Content + +swagger:response getVsNoContent +*/ +type GetVsNoContent struct { + + /* + In: Body + */ + Payload *models.VirtualServerList `json:"body,omitempty"` +} + +// NewGetVsNoContent creates GetVsNoContent with default headers values +func NewGetVsNoContent() *GetVsNoContent { + + return &GetVsNoContent{} +} + +// WithPayload adds the payload to the get vs no content response +func (o *GetVsNoContent) WithPayload(payload *models.VirtualServerList) *GetVsNoContent { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get vs no content response +func (o *GetVsNoContent) SetPayload(payload *models.VirtualServerList) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetVsNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(204) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_urlbuilder.go index 917308dcf..3c2fbac4d 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_urlbuilder.go @@ -15,7 +15,9 @@ import ( // GetVsURL generates an URL for the get vs operation type GetVsURL struct { - Stats *bool + Healthcheck *bool + Snapshot *bool + Stats *bool _basePath string // avoid unkeyed usage @@ -51,6 +53,22 @@ func (o *GetVsURL) Build() (*url.URL, error) { qs := make(url.Values) + var healthcheckQ string + if o.Healthcheck != nil { + healthcheckQ = swag.FormatBool(*o.Healthcheck) + } + if healthcheckQ != "" { + qs.Set("healthcheck", healthcheckQ) + } + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + var statsQ string if o.Stats != nil { statsQ = swag.FormatBool(*o.Stats) diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_parameters.go index 0fb9cf281..37a8d01a6 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_parameters.go @@ -22,10 +22,13 @@ func NewGetVsVipPortLaddrParams() GetVsVipPortLaddrParams { var ( // initialize parameters with default values - statsDefault = bool(false) + snapshotDefault = bool(true) + statsDefault = bool(false) ) return GetVsVipPortLaddrParams{ + Snapshot: &snapshotDefault, + Stats: &statsDefault, } } @@ -44,6 +47,11 @@ type GetVsVipPortLaddrParams struct { In: path */ VipPort string + /* + In: query + Default: true + */ + Snapshot *bool /* In: query Default: false @@ -67,6 +75,11 @@ func (o *GetVsVipPortLaddrParams) BindRequest(r *http.Request, route *middleware res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + qStats, qhkStats, _ := qs.GetOK("stats") if err := o.bindStats(qStats, qhkStats, route.Formats); err != nil { res = append(res, err) @@ -91,6 +104,30 @@ func (o *GetVsVipPortLaddrParams) bindVipPort(rawData []string, hasKey bool, for return nil } +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *GetVsVipPortLaddrParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsVipPortLaddrParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} + // bindStats binds and validates parameter Stats from query. func (o *GetVsVipPortLaddrParams) bindStats(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_urlbuilder.go index 053283e75..c71f2fa33 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_laddr_urlbuilder.go @@ -18,7 +18,8 @@ import ( type GetVsVipPortLaddrURL struct { VipPort string - Stats *bool + Snapshot *bool + Stats *bool _basePath string // avoid unkeyed usage @@ -61,6 +62,14 @@ func (o *GetVsVipPortLaddrURL) Build() (*url.URL, error) { qs := make(url.Values) + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + var statsQ string if o.Stats != nil { statsQ = swag.FormatBool(*o.Stats) diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_parameters.go index a917c108a..e5e06dc91 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_parameters.go @@ -22,10 +22,16 @@ func NewGetVsVipPortParams() GetVsVipPortParams { var ( // initialize parameters with default values - statsDefault = bool(false) + healthcheckDefault = bool(false) + snapshotDefault = bool(true) + statsDefault = bool(false) ) return GetVsVipPortParams{ + Healthcheck: &healthcheckDefault, + + Snapshot: &snapshotDefault, + Stats: &statsDefault, } } @@ -48,6 +54,16 @@ type GetVsVipPortParams struct { In: query Default: false */ + Healthcheck *bool + /* + In: query + Default: true + */ + Snapshot *bool + /* + In: query + Default: false + */ Stats *bool } @@ -67,6 +83,16 @@ func (o *GetVsVipPortParams) BindRequest(r *http.Request, route *middleware.Matc res = append(res, err) } + qHealthcheck, qhkHealthcheck, _ := qs.GetOK("healthcheck") + if err := o.bindHealthcheck(qHealthcheck, qhkHealthcheck, route.Formats); err != nil { + res = append(res, err) + } + + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + qStats, qhkStats, _ := qs.GetOK("stats") if err := o.bindStats(qStats, qhkStats, route.Formats); err != nil { res = append(res, err) @@ -91,6 +117,54 @@ func (o *GetVsVipPortParams) bindVipPort(rawData []string, hasKey bool, formats return nil } +// bindHealthcheck binds and validates parameter Healthcheck from query. +func (o *GetVsVipPortParams) bindHealthcheck(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsVipPortParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("healthcheck", "query", "bool", raw) + } + o.Healthcheck = &value + + return nil +} + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *GetVsVipPortParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsVipPortParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} + // bindStats binds and validates parameter Stats from query. func (o *GetVsVipPortParams) bindStats(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_parameters.go index 422d711e1..a7d68d0d3 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_parameters.go @@ -22,10 +22,13 @@ func NewGetVsVipPortRsParams() GetVsVipPortRsParams { var ( // initialize parameters with default values - statsDefault = bool(false) + snapshotDefault = bool(true) + statsDefault = bool(false) ) return GetVsVipPortRsParams{ + Snapshot: &snapshotDefault, + Stats: &statsDefault, } } @@ -44,6 +47,11 @@ type GetVsVipPortRsParams struct { In: path */ VipPort string + /* + In: query + Default: true + */ + Snapshot *bool /* In: query Default: false @@ -67,6 +75,11 @@ func (o *GetVsVipPortRsParams) BindRequest(r *http.Request, route *middleware.Ma res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + qStats, qhkStats, _ := qs.GetOK("stats") if err := o.bindStats(qStats, qhkStats, route.Formats); err != nil { res = append(res, err) @@ -91,6 +104,30 @@ func (o *GetVsVipPortRsParams) bindVipPort(rawData []string, hasKey bool, format return nil } +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *GetVsVipPortRsParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetVsVipPortRsParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} + // bindStats binds and validates parameter Stats from query. func (o *GetVsVipPortRsParams) bindStats(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_urlbuilder.go index f97a471ed..30936c327 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_rs_urlbuilder.go @@ -18,7 +18,8 @@ import ( type GetVsVipPortRsURL struct { VipPort string - Stats *bool + Snapshot *bool + Stats *bool _basePath string // avoid unkeyed usage @@ -61,6 +62,14 @@ func (o *GetVsVipPortRsURL) Build() (*url.URL, error) { qs := make(url.Values) + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + var statsQ string if o.Stats != nil { statsQ = swag.FormatBool(*o.Stats) diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_urlbuilder.go index 699572745..e09106c05 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/get_vs_vip_port_urlbuilder.go @@ -18,7 +18,9 @@ import ( type GetVsVipPortURL struct { VipPort string - Stats *bool + Healthcheck *bool + Snapshot *bool + Stats *bool _basePath string // avoid unkeyed usage @@ -61,6 +63,22 @@ func (o *GetVsVipPortURL) Build() (*url.URL, error) { qs := make(url.Values) + var healthcheckQ string + if o.Healthcheck != nil { + healthcheckQ = swag.FormatBool(*o.Healthcheck) + } + if healthcheckQ != "" { + qs.Set("healthcheck", healthcheckQ) + } + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + var statsQ string if o.Stats != nil { statsQ = swag.FormatBool(*o.Stats) diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_parameters.go index 948a9f38b..b92f98b6f 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_parameters.go @@ -12,17 +12,25 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" "github.com/go-openapi/validate" "github.com/dpvs-agent/models" ) // NewPostVsVipPortRsParams creates a new PostVsVipPortRsParams object -// -// There are no default values defined in the spec. +// with the default values initialized. func NewPostVsVipPortRsParams() PostVsVipPortRsParams { - return PostVsVipPortRsParams{} + var ( + // initialize parameters with default values + + snapshotDefault = bool(true) + ) + + return PostVsVipPortRsParams{ + Snapshot: &snapshotDefault, + } } // PostVsVipPortRsParams contains all the bound params for the post vs vip port rs operation @@ -43,6 +51,11 @@ type PostVsVipPortRsParams struct { In: body */ Rss *models.RealServerTinyList + /* + In: query + Default: true + */ + Snapshot *bool } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -54,6 +67,8 @@ func (o *PostVsVipPortRsParams) BindRequest(r *http.Request, route *middleware.M o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + rVipPort, rhkVipPort, _ := route.Params.GetOK("VipPort") if err := o.bindVipPort(rVipPort, rhkVipPort, route.Formats); err != nil { res = append(res, err) @@ -80,6 +95,11 @@ func (o *PostVsVipPortRsParams) BindRequest(r *http.Request, route *middleware.M } } } + + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -99,3 +119,27 @@ func (o *PostVsVipPortRsParams) bindVipPort(rawData []string, hasKey bool, forma return nil } + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *PostVsVipPortRsParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewPostVsVipPortRsParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_urlbuilder.go index 1c7f7b01e..c596ec741 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/post_vs_vip_port_rs_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // PostVsVipPortRsURL generates an URL for the post vs vip port rs operation type PostVsVipPortRsURL struct { VipPort string + Snapshot *bool + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *PostVsVipPortRsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_parameters.go index 94c97a03a..a4089848d 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_parameters.go @@ -12,17 +12,25 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" "github.com/go-openapi/validate" "github.com/dpvs-agent/models" ) // NewPutVsVipPortLaddrParams creates a new PutVsVipPortLaddrParams object -// -// There are no default values defined in the spec. +// with the default values initialized. func NewPutVsVipPortLaddrParams() PutVsVipPortLaddrParams { - return PutVsVipPortLaddrParams{} + var ( + // initialize parameters with default values + + snapshotDefault = bool(true) + ) + + return PutVsVipPortLaddrParams{ + Snapshot: &snapshotDefault, + } } // PutVsVipPortLaddrParams contains all the bound params for the put vs vip port laddr operation @@ -39,6 +47,11 @@ type PutVsVipPortLaddrParams struct { In: path */ VipPort string + /* + In: query + Default: true + */ + Snapshot *bool /* In: body */ @@ -54,11 +67,18 @@ func (o *PutVsVipPortLaddrParams) BindRequest(r *http.Request, route *middleware o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + rVipPort, rhkVipPort, _ := route.Params.GetOK("VipPort") if err := o.bindVipPort(rVipPort, rhkVipPort, route.Formats); err != nil { res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + if runtime.HasBody(r) { defer r.Body.Close() var body models.LocalAddressSpecTiny @@ -99,3 +119,27 @@ func (o *PutVsVipPortLaddrParams) bindVipPort(rawData []string, hasKey bool, for return nil } + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *PutVsVipPortLaddrParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewPutVsVipPortLaddrParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_urlbuilder.go index c0dc2d7d5..34bd5f56b 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_laddr_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // PutVsVipPortLaddrURL generates an URL for the put vs vip port laddr operation type PutVsVipPortLaddrURL struct { VipPort string + Snapshot *bool + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *PutVsVipPortLaddrURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_parameters.go index 8dc0c2030..4aa52d23a 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_parameters.go @@ -12,17 +12,25 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" "github.com/go-openapi/validate" "github.com/dpvs-agent/models" ) // NewPutVsVipPortParams creates a new PutVsVipPortParams object -// -// There are no default values defined in the spec. +// with the default values initialized. func NewPutVsVipPortParams() PutVsVipPortParams { - return PutVsVipPortParams{} + var ( + // initialize parameters with default values + + snapshotDefault = bool(true) + ) + + return PutVsVipPortParams{ + Snapshot: &snapshotDefault, + } } // PutVsVipPortParams contains all the bound params for the put vs vip port operation @@ -39,6 +47,11 @@ type PutVsVipPortParams struct { In: path */ VipPort string + /* + In: query + Default: true + */ + Snapshot *bool /* In: body */ @@ -54,11 +67,18 @@ func (o *PutVsVipPortParams) BindRequest(r *http.Request, route *middleware.Matc o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + rVipPort, rhkVipPort, _ := route.Params.GetOK("VipPort") if err := o.bindVipPort(rVipPort, rhkVipPort, route.Formats); err != nil { res = append(res, err) } + qSnapshot, qhkSnapshot, _ := qs.GetOK("snapshot") + if err := o.bindSnapshot(qSnapshot, qhkSnapshot, route.Formats); err != nil { + res = append(res, err) + } + if runtime.HasBody(r) { defer r.Body.Close() var body models.VirtualServerSpecTiny @@ -99,3 +119,27 @@ func (o *PutVsVipPortParams) bindVipPort(rawData []string, hasKey bool, formats return nil } + +// bindSnapshot binds and validates parameter Snapshot from query. +func (o *PutVsVipPortParams) bindSnapshot(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewPutVsVipPortParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("snapshot", "query", "bool", raw) + } + o.Snapshot = &value + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health.go new file mode 100644 index 000000000..18770d342 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package virtualserver + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// PutVsVipPortRsHealthHandlerFunc turns a function with the right signature into a put vs vip port rs health handler +type PutVsVipPortRsHealthHandlerFunc func(PutVsVipPortRsHealthParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn PutVsVipPortRsHealthHandlerFunc) Handle(params PutVsVipPortRsHealthParams) middleware.Responder { + return fn(params) +} + +// PutVsVipPortRsHealthHandler interface for that can handle valid put vs vip port rs health params +type PutVsVipPortRsHealthHandler interface { + Handle(PutVsVipPortRsHealthParams) middleware.Responder +} + +// NewPutVsVipPortRsHealth creates a new http.Handler for the put vs vip port rs health operation +func NewPutVsVipPortRsHealth(ctx *middleware.Context, handler PutVsVipPortRsHealthHandler) *PutVsVipPortRsHealth { + return &PutVsVipPortRsHealth{Context: ctx, Handler: handler} +} + +/* + PutVsVipPortRsHealth swagger:route PUT /vs/{VipPort}/rs/health virtualserver putVsVipPortRsHealth + +dpvs healthcheck update rs weight +*/ +type PutVsVipPortRsHealth struct { + Context *middleware.Context + Handler PutVsVipPortRsHealthHandler +} + +func (o *PutVsVipPortRsHealth) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewPutVsVipPortRsHealthParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_parameters.go new file mode 100644 index 000000000..ca2d0f9fd --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_parameters.go @@ -0,0 +1,134 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package virtualserver + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/dpvs-agent/models" +) + +// NewPutVsVipPortRsHealthParams creates a new PutVsVipPortRsHealthParams object +// +// There are no default values defined in the spec. +func NewPutVsVipPortRsHealthParams() PutVsVipPortRsHealthParams { + + return PutVsVipPortRsHealthParams{} +} + +// PutVsVipPortRsHealthParams contains all the bound params for the put vs vip port rs health operation +// typically these are obtained from a http.Request +// +// swagger:parameters PutVsVipPortRsHealth +type PutVsVipPortRsHealthParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + VipPort string + /* + In: body + */ + Rss *models.RealServerTinyList + /* + Required: true + In: query + */ + Version string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewPutVsVipPortRsHealthParams() beforehand. +func (o *PutVsVipPortRsHealthParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + rVipPort, rhkVipPort, _ := route.Params.GetOK("VipPort") + if err := o.bindVipPort(rVipPort, rhkVipPort, route.Formats); err != nil { + res = append(res, err) + } + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.RealServerTinyList + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("rss", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Rss = &body + } + } + } + + qVersion, qhkVersion, _ := qs.GetOK("version") + if err := o.bindVersion(qVersion, qhkVersion, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindVipPort binds and validates parameter VipPort from path. +func (o *PutVsVipPortRsHealthParams) bindVipPort(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.VipPort = raw + + return nil +} + +// bindVersion binds and validates parameter Version from query. +func (o *PutVsVipPortRsHealthParams) bindVersion(rawData []string, hasKey bool, formats strfmt.Registry) error { + if !hasKey { + return errors.Required("version", "query", rawData) + } + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // AllowEmptyValue: false + + if err := validate.RequiredString("version", "query", raw); err != nil { + return err + } + o.Version = raw + + return nil +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_responses.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_responses.go new file mode 100644 index 000000000..b00b1d3fa --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_responses.go @@ -0,0 +1,213 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package virtualserver + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/dpvs-agent/models" +) + +// PutVsVipPortRsHealthOKCode is the HTTP code returned for type PutVsVipPortRsHealthOK +const PutVsVipPortRsHealthOKCode int = 200 + +/* +PutVsVipPortRsHealthOK Success + +swagger:response putVsVipPortRsHealthOK +*/ +type PutVsVipPortRsHealthOK struct { +} + +// NewPutVsVipPortRsHealthOK creates PutVsVipPortRsHealthOK with default headers values +func NewPutVsVipPortRsHealthOK() *PutVsVipPortRsHealthOK { + + return &PutVsVipPortRsHealthOK{} +} + +// WriteResponse to the client +func (o *PutVsVipPortRsHealthOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(200) +} + +// PutVsVipPortRsHealthUnexpectedCode is the HTTP code returned for type PutVsVipPortRsHealthUnexpected +const PutVsVipPortRsHealthUnexpectedCode int = 270 + +/* +PutVsVipPortRsHealthUnexpected the rss-config parameter is outdated, update nothing and return the latest rs info + +swagger:response putVsVipPortRsHealthUnexpected +*/ +type PutVsVipPortRsHealthUnexpected struct { + + /* + In: Body + */ + Payload *models.VirtualServerSpecExpand `json:"body,omitempty"` +} + +// NewPutVsVipPortRsHealthUnexpected creates PutVsVipPortRsHealthUnexpected with default headers values +func NewPutVsVipPortRsHealthUnexpected() *PutVsVipPortRsHealthUnexpected { + + return &PutVsVipPortRsHealthUnexpected{} +} + +// WithPayload adds the payload to the put vs vip port rs health unexpected response +func (o *PutVsVipPortRsHealthUnexpected) WithPayload(payload *models.VirtualServerSpecExpand) *PutVsVipPortRsHealthUnexpected { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the put vs vip port rs health unexpected response +func (o *PutVsVipPortRsHealthUnexpected) SetPayload(payload *models.VirtualServerSpecExpand) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PutVsVipPortRsHealthUnexpected) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(270) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// PutVsVipPortRsHealthInvalidFrontendCode is the HTTP code returned for type PutVsVipPortRsHealthInvalidFrontend +const PutVsVipPortRsHealthInvalidFrontendCode int = 460 + +/* +PutVsVipPortRsHealthInvalidFrontend Invalid frontend in service configuration + +swagger:response putVsVipPortRsHealthInvalidFrontend +*/ +type PutVsVipPortRsHealthInvalidFrontend struct { + + /* + In: Body + */ + Payload models.Error `json:"body,omitempty"` +} + +// NewPutVsVipPortRsHealthInvalidFrontend creates PutVsVipPortRsHealthInvalidFrontend with default headers values +func NewPutVsVipPortRsHealthInvalidFrontend() *PutVsVipPortRsHealthInvalidFrontend { + + return &PutVsVipPortRsHealthInvalidFrontend{} +} + +// WithPayload adds the payload to the put vs vip port rs health invalid frontend response +func (o *PutVsVipPortRsHealthInvalidFrontend) WithPayload(payload models.Error) *PutVsVipPortRsHealthInvalidFrontend { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the put vs vip port rs health invalid frontend response +func (o *PutVsVipPortRsHealthInvalidFrontend) SetPayload(payload models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PutVsVipPortRsHealthInvalidFrontend) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(460) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// PutVsVipPortRsHealthInvalidBackendCode is the HTTP code returned for type PutVsVipPortRsHealthInvalidBackend +const PutVsVipPortRsHealthInvalidBackendCode int = 461 + +/* +PutVsVipPortRsHealthInvalidBackend Invalid backend in service configuration + +swagger:response putVsVipPortRsHealthInvalidBackend +*/ +type PutVsVipPortRsHealthInvalidBackend struct { + + /* + In: Body + */ + Payload models.Error `json:"body,omitempty"` +} + +// NewPutVsVipPortRsHealthInvalidBackend creates PutVsVipPortRsHealthInvalidBackend with default headers values +func NewPutVsVipPortRsHealthInvalidBackend() *PutVsVipPortRsHealthInvalidBackend { + + return &PutVsVipPortRsHealthInvalidBackend{} +} + +// WithPayload adds the payload to the put vs vip port rs health invalid backend response +func (o *PutVsVipPortRsHealthInvalidBackend) WithPayload(payload models.Error) *PutVsVipPortRsHealthInvalidBackend { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the put vs vip port rs health invalid backend response +func (o *PutVsVipPortRsHealthInvalidBackend) SetPayload(payload models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PutVsVipPortRsHealthInvalidBackend) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(461) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// PutVsVipPortRsHealthFailureCode is the HTTP code returned for type PutVsVipPortRsHealthFailure +const PutVsVipPortRsHealthFailureCode int = 500 + +/* +PutVsVipPortRsHealthFailure Service deletion failed + +swagger:response putVsVipPortRsHealthFailure +*/ +type PutVsVipPortRsHealthFailure struct { + + /* + In: Body + */ + Payload models.Error `json:"body,omitempty"` +} + +// NewPutVsVipPortRsHealthFailure creates PutVsVipPortRsHealthFailure with default headers values +func NewPutVsVipPortRsHealthFailure() *PutVsVipPortRsHealthFailure { + + return &PutVsVipPortRsHealthFailure{} +} + +// WithPayload adds the payload to the put vs vip port rs health failure response +func (o *PutVsVipPortRsHealthFailure) WithPayload(payload models.Error) *PutVsVipPortRsHealthFailure { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the put vs vip port rs health failure response +func (o *PutVsVipPortRsHealthFailure) SetPayload(payload models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *PutVsVipPortRsHealthFailure) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_urlbuilder.go new file mode 100644 index 000000000..9ae7a46a7 --- /dev/null +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_health_urlbuilder.go @@ -0,0 +1,110 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package virtualserver + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// PutVsVipPortRsHealthURL generates an URL for the put vs vip port rs health operation +type PutVsVipPortRsHealthURL struct { + VipPort string + + Version string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *PutVsVipPortRsHealthURL) WithBasePath(bp string) *PutVsVipPortRsHealthURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *PutVsVipPortRsHealthURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *PutVsVipPortRsHealthURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/vs/{VipPort}/rs/health" + + vipPort := o.VipPort + if vipPort != "" { + _path = strings.Replace(_path, "{VipPort}", vipPort, -1) + } else { + return nil, errors.New("vipPort is required on PutVsVipPortRsHealthURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/v2" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + versionQ := o.Version + if versionQ != "" { + qs.Set("version", versionQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *PutVsVipPortRsHealthURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *PutVsVipPortRsHealthURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *PutVsVipPortRsHealthURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on PutVsVipPortRsHealthURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on PutVsVipPortRsHealthURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *PutVsVipPortRsHealthURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_parameters.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_parameters.go index 0e36def24..6a0bc5188 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_parameters.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_parameters.go @@ -12,25 +12,17 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" "github.com/go-openapi/validate" "github.com/dpvs-agent/models" ) // NewPutVsVipPortRsParams creates a new PutVsVipPortRsParams object -// with the default values initialized. +// +// There are no default values defined in the spec. func NewPutVsVipPortRsParams() PutVsVipPortRsParams { - var ( - // initialize parameters with default values - - healthcheckDefault = bool(false) - ) - - return PutVsVipPortRsParams{ - Healthcheck: &healthcheckDefault, - } + return PutVsVipPortRsParams{} } // PutVsVipPortRsParams contains all the bound params for the put vs vip port rs operation @@ -47,11 +39,6 @@ type PutVsVipPortRsParams struct { In: path */ VipPort string - /* - In: query - Default: false - */ - Healthcheck *bool /* In: body */ @@ -67,18 +54,11 @@ func (o *PutVsVipPortRsParams) BindRequest(r *http.Request, route *middleware.Ma o.HTTPRequest = r - qs := runtime.Values(r.URL.Query()) - rVipPort, rhkVipPort, _ := route.Params.GetOK("VipPort") if err := o.bindVipPort(rVipPort, rhkVipPort, route.Formats); err != nil { res = append(res, err) } - qHealthcheck, qhkHealthcheck, _ := qs.GetOK("healthcheck") - if err := o.bindHealthcheck(qHealthcheck, qhkHealthcheck, route.Formats); err != nil { - res = append(res, err) - } - if runtime.HasBody(r) { defer r.Body.Close() var body models.RealServerTinyList @@ -119,27 +99,3 @@ func (o *PutVsVipPortRsParams) bindVipPort(rawData []string, hasKey bool, format return nil } - -// bindHealthcheck binds and validates parameter Healthcheck from query. -func (o *PutVsVipPortRsParams) bindHealthcheck(rawData []string, hasKey bool, formats strfmt.Registry) error { - var raw string - if len(rawData) > 0 { - raw = rawData[len(rawData)-1] - } - - // Required: false - // AllowEmptyValue: false - - if raw == "" { // empty values pass all other validations - // Default values have been previously initialized by NewPutVsVipPortRsParams() - return nil - } - - value, err := swag.ConvertBool(raw) - if err != nil { - return errors.InvalidType("healthcheck", "query", "bool", raw) - } - o.Healthcheck = &value - - return nil -} diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_urlbuilder.go index d906fae36..90b4750e4 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_rs_urlbuilder.go @@ -10,16 +10,12 @@ import ( "net/url" golangswaggerpaths "path" "strings" - - "github.com/go-openapi/swag" ) // PutVsVipPortRsURL generates an URL for the put vs vip port rs operation type PutVsVipPortRsURL struct { VipPort string - Healthcheck *bool - _basePath string // avoid unkeyed usage _ struct{} @@ -59,18 +55,6 @@ func (o *PutVsVipPortRsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) - qs := make(url.Values) - - var healthcheckQ string - if o.Healthcheck != nil { - healthcheckQ = swag.FormatBool(*o.Healthcheck) - } - if healthcheckQ != "" { - qs.Set("healthcheck", healthcheckQ) - } - - _result.RawQuery = qs.Encode() - return &_result, nil } diff --git a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_urlbuilder.go b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_urlbuilder.go index 01adfb16c..a88b8ca67 100644 --- a/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_urlbuilder.go +++ b/tools/dpvs-agent/restapi/operations/virtualserver/put_vs_vip_port_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // PutVsVipPortURL generates an URL for the put vs vip port operation type PutVsVipPortURL struct { VipPort string + Snapshot *bool + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *PutVsVipPortURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var snapshotQ string + if o.Snapshot != nil { + snapshotQ = swag.FormatBool(*o.Snapshot) + } + if snapshotQ != "" { + qs.Set("snapshot", snapshotQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/tools/healthcheck/Makefile b/tools/healthcheck/Makefile index 6e866e140..fe4d9400a 100644 --- a/tools/healthcheck/Makefile +++ b/tools/healthcheck/Makefile @@ -1,7 +1,7 @@ TARGET := healthcheck GO ?= go -LD_FLAGS = -ldflags="-s -w" +LD_FLAGS = -ldflags="-s -w -X main.buildVersion=$(git rev-parse --short HEAD)" GO_BUILD = CGO_ENABLED=0 $(GO) build $(LD_FLAGS) GO_CLEAN = $(GO) clean diff --git a/tools/healthcheck/pkg/helthcheck/checker.go b/tools/healthcheck/pkg/helthcheck/checker.go index 745924b7b..e756fd7c8 100644 --- a/tools/healthcheck/pkg/helthcheck/checker.go +++ b/tools/healthcheck/pkg/helthcheck/checker.go @@ -24,7 +24,7 @@ import ( log "github.com/golang/glog" ) -const uweightDefault uint16 = 1 +const uweightDefault uint16 = 100 var ( proxyProtoV1LocalCmd = "PROXY UNKNOWN\r\n" @@ -35,7 +35,7 @@ var ( ) // Checks provides a map of healthcheck configurations. -type Checkers struct { +type CheckerConfigs struct { Configs map[Id]*CheckerConfig } @@ -69,7 +69,7 @@ func NewChecker(notify chan<- *Notification, state State, weight uint16) *Checke state: state, uweight: weight, notify: notify, - update: make(chan CheckerConfig, 1), + update: make(chan CheckerConfig, 2), quit: make(chan bool, 1), } } @@ -79,6 +79,7 @@ func (hc *Checker) Status() Status { hc.lock.RLock() defer hc.lock.RUnlock() status := Status{ + Version: hc.Version, LastCheck: hc.start, Failures: hc.failures, Successes: hc.successes, @@ -95,14 +96,55 @@ func (hc *Checker) Status() Status { } func (hc *Checker) updateConfig(conf *CheckerConfig) { - hc.CheckerConfig = *conf - if conf.State != StateUnhealthy { + // Note: + // The conf::Weight must be the original weight not modified by healthcheck program, + // while conf::State reflects the health state derived from the inhibited flag set by + // the healthcheck program. + + //log.Infof("[updateConfig] id(%v) version(%d->%d) target(%v) %v->%v weight(%d->%d)\n", conf.Id, + // hc.Version, conf.Version, conf.Target, hc.State, conf.State, hc.Weight, conf.Weight) + + if len(hc.Id) == 0 { + hc.CheckerConfig = *conf + return + } + + if conf.Version < hc.Version { + return + } + + hc.State = conf.State + hc.lock.Lock() + state := hc.state + hc.state = conf.State + hc.lock.Unlock() + if state != conf.State { + log.Warningf("%v: healthcheck's state changed externally %v -> %v", + hc.Id, state, conf.State) + } + + if conf.State != StateUnhealthy || conf.State == StateUnhealthy && conf.Weight > 0 { + // Update all the checker configs when conf's state is healthy. + hc.Weight = conf.Weight + if conf.Interval > 0 { + hc.Interval = conf.Interval + } + if conf.Timeout > 0 { + hc.Timeout = conf.Timeout + } + if conf.Retry > 0 { + hc.Retry = conf.Retry + } + hc.lock.Lock() weight := hc.uweight hc.uweight = conf.Weight hc.lock.Unlock() + + hc.Version = conf.Version if weight != conf.Weight { - log.Infof("%v: user weight changed %d -> %d", hc.Id, weight, conf.Weight) + log.Warningf("%v: healthcheck's user weight changed %d -> %d", + hc.Id, weight, conf.Weight) } } } @@ -154,8 +196,10 @@ func (hc *Checker) healthcheck() { } status := "SUCCESS" + state := StateHealthy if !result.Success { status = "FAILURE" + state = StateUnhealthy } log.Infof("%v: %s: %v", hc.Id, status, result) @@ -164,15 +208,12 @@ func (hc *Checker) healthcheck() { hc.start = start hc.result = result - var state State if result.Success { - state = StateHealthy hc.failed = 0 hc.successes++ } else { hc.failed++ hc.failures++ - state = StateUnhealthy } if hc.state == StateHealthy && hc.failed > 0 && hc.failed <= uint64(hc.CheckerConfig.Retry) { @@ -222,7 +263,7 @@ func (hc *Checker) Run(start <-chan time.Time) { return case config := <-hc.update: - if hc.Interval != config.Interval { + if config.Interval > 0 && hc.Interval != config.Interval { ticker.Stop() if start != nil { <-start @@ -255,6 +296,6 @@ func (hc *Checker) Update(config *CheckerConfig) { select { case hc.update <- *config: default: - log.Warningf("Unable to update %v, last update still queued", hc.Id) + log.Warningf("Unable to update %v, last two update still queued", hc.Id) } } diff --git a/tools/healthcheck/pkg/helthcheck/configs.go b/tools/healthcheck/pkg/helthcheck/configs.go index 7759928b5..375261cb6 100644 --- a/tools/healthcheck/pkg/helthcheck/configs.go +++ b/tools/healthcheck/pkg/helthcheck/configs.go @@ -63,6 +63,10 @@ func DefaultServerConfig() ServerConfig { type CheckerConfig struct { Id + // Version denotes the virtual service version. It used to protect the vs from + // incorrect weight updates by healthcheck when the vs's weight changed externally. + Version uint64 + Target State Weight uint16 @@ -76,11 +80,12 @@ type CheckerConfig struct { var DefaultCheckConfig CheckerConfig // NewConfig returns an initialised Config. -func NewCheckerConfig(id *Id, checker CheckMethod, - target *Target, state State, weight uint16, - interval, timeout time.Duration, retry uint) *CheckerConfig { +func NewCheckerConfig(id *Id, version uint64, checker CheckMethod, + target *Target, state State, weight uint16, interval, + timeout time.Duration, retry uint) *CheckerConfig { config := CheckerConfig{ Id: *id, + Version: version, Target: *target, State: state, Weight: weight, @@ -89,6 +94,8 @@ func NewCheckerConfig(id *Id, checker CheckMethod, Timeout: timeout, Retry: retry, } - config.BindConfig(&config) + if config.CheckMethod != nil { + config.BindConfig(&config) + } return &config } diff --git a/tools/healthcheck/pkg/helthcheck/http_checker.go b/tools/healthcheck/pkg/helthcheck/http_checker.go index 97b7d5102..099d1ed2c 100644 --- a/tools/healthcheck/pkg/helthcheck/http_checker.go +++ b/tools/healthcheck/pkg/helthcheck/http_checker.go @@ -64,6 +64,7 @@ func NewHttpChecker(method, host, uri string, proxyProto int) *HttpChecker { } return &HttpChecker{ Method: method, + Host: host, Uri: uri, ResponseCodes: []HttpCodeRange{{200, 299}, {300, 399}, {400, 499}}, Response: "", diff --git a/tools/healthcheck/pkg/helthcheck/http_checker_test.go b/tools/healthcheck/pkg/helthcheck/http_checker_test.go index 0fbbff3b4..5d3b3d491 100644 --- a/tools/healthcheck/pkg/helthcheck/http_checker_test.go +++ b/tools/healthcheck/pkg/helthcheck/http_checker_test.go @@ -58,7 +58,7 @@ func TestHttpChecker(t *testing.T) { } */ id := Id(target.String()) - config := NewCheckerConfig(&id, checker, &target, StateUnknown, + config := NewCheckerConfig(&id, 0, checker, &target, StateUnknown, 0, 3*time.Second, 2*time.Second, 3) result := checker.Check(target, config.Timeout) fmt.Printf("[ HTTP ] %s ==> %v\n", target, result) @@ -68,14 +68,14 @@ func TestHttpChecker(t *testing.T) { checker := NewHttpChecker("", "", "", 1) checker.Host = target.Addr() id := Id(target.String()) - config := NewCheckerConfig(&id, checker, &target, StateUnknown, + config := NewCheckerConfig(&id, 0, checker, &target, StateUnknown, 0, 3*time.Second, 2*time.Second, 3) result := checker.Check(target, config.Timeout) fmt.Printf("[ HTTP(PPv1) ] %s ==> %v\n", target, result) checker2 := NewHttpChecker("", "", "", 2) checker2.Host = target.Addr() id2 := Id(target.String()) - config2 := NewCheckerConfig(&id2, checker2, &target, StateUnknown, + config2 := NewCheckerConfig(&id2, 0, checker2, &target, StateUnknown, 0, 3*time.Second, 2*time.Second, 3) result2 := checker2.Check(target, config2.Timeout) fmt.Printf("[ HTTP(PPv2) ] %s ==> %v\n", target, result2) @@ -90,7 +90,7 @@ func TestHttpChecker(t *testing.T) { checker.Secure = true } id := Id(host) - config := NewCheckerConfig(&id, checker, &Target{}, StateUnknown, + config := NewCheckerConfig(&id, 0, checker, &Target{}, StateUnknown, 0, 3*time.Second, 2*time.Second, 3) result := checker.Check(Target{}, config.Timeout) if result.Success == false { diff --git a/tools/healthcheck/pkg/helthcheck/ping_checker.go b/tools/healthcheck/pkg/helthcheck/ping_checker.go index dae8eda92..a1401f66d 100644 --- a/tools/healthcheck/pkg/helthcheck/ping_checker.go +++ b/tools/healthcheck/pkg/helthcheck/ping_checker.go @@ -21,6 +21,7 @@ package hc import ( "bytes" + "encoding/binary" "fmt" "math/rand" "net" @@ -113,8 +114,8 @@ func newICMPv4EchoRequest(id, seqnum, msglen uint16, filler []byte) icmpMsg { cs := icmpChecksum(msg) // place checksum back in header; using ^= avoids the assumption that the // checksum bytes are zero - msg[2] ^= uint8(cs & 0xff) - msg[3] ^= uint8(cs >> 8) + cs ^= binary.BigEndian.Uint16(msg[2:4]) + binary.BigEndian.PutUint16(msg[2:4], cs) return msg } @@ -122,13 +123,13 @@ func icmpChecksum(msg icmpMsg) uint16 { cklen := len(msg) s := uint32(0) for i := 0; i < cklen-1; i += 2 { - s += uint32(msg[i+1])<<8 | uint32(msg[i]) + s += uint32(binary.BigEndian.Uint16(msg[i : i+2])) } if cklen&1 == 1 { - s += uint32(msg[cklen-1]) + s += uint32(msg[cklen-1]) << 8 } s = (s >> 16) + (s & 0xffff) - s = s + (s >> 16) + s += (s >> 16) return uint16(^s) } @@ -175,10 +176,13 @@ func exchangeICMPEcho(network string, ip net.IP, timeout time.Duration, echo icm c.SetDeadline(time.Now().Add(timeout)) reply := make([]byte, 256) for { - _, addr, err := c.ReadFrom(reply) + n, addr, err := c.ReadFrom(reply) if err != nil { return err } + if n < 0 || n > len(reply) { + return fmt.Errorf("Unexpect ICMP reply len %d", n) + } if !ip.Equal(net.ParseIP(addr.String())) { continue } @@ -191,9 +195,9 @@ func exchangeICMPEcho(network string, ip net.IP, timeout time.Duration, echo icm continue } if reply[0] == ICMP4_ECHO_REPLY { - cs := icmpChecksum(reply) + cs := icmpChecksum(reply[:n]) if cs != 0 { - return fmt.Errorf("Bad ICMP checksum: %x", rchksum) + return fmt.Errorf("Bad ICMP checksum: %x, len: %d, data: %v", rchksum, n, reply[:n]) } } // TODO(angusc): Validate checksum for IPv6 diff --git a/tools/healthcheck/pkg/helthcheck/ping_checker_test.go b/tools/healthcheck/pkg/helthcheck/ping_checker_test.go index 7b86e665a..b462c1dd7 100644 --- a/tools/healthcheck/pkg/helthcheck/ping_checker_test.go +++ b/tools/healthcheck/pkg/helthcheck/ping_checker_test.go @@ -37,7 +37,7 @@ func TestPingChecker(t *testing.T) { for _, target := range ping_targets { checker := NewPingChecker() id := Id(target.IP.String()) - config := NewCheckerConfig(&id, checker, + config := NewCheckerConfig(&id, 0, checker, &target, StateUnknown, 0, 3*time.Second, 1*time.Second, 3) result := checker.Check(target, config.Timeout) diff --git a/tools/healthcheck/pkg/helthcheck/server.go b/tools/healthcheck/pkg/helthcheck/server.go index d7af16f08..edcbb6561 100644 --- a/tools/healthcheck/pkg/helthcheck/server.go +++ b/tools/healthcheck/pkg/helthcheck/server.go @@ -35,6 +35,7 @@ type Server struct { healthchecks map[Id]*Checker configs chan map[Id]*CheckerConfig notify chan *Notification + resync chan *CheckerConfig quit chan bool } @@ -63,6 +64,7 @@ func NewServer(cfg *ServerConfig) *Server { healthchecks: make(map[Id]*Checker), notify: make(chan *Notification, cfg.NotifyChannelSize), configs: make(chan map[Id]*CheckerConfig), + resync: make(chan *CheckerConfig, cfg.NotifyChannelSize), quit: make(chan bool, 1), } @@ -78,8 +80,10 @@ func (s *Server) NewChecker(typ lb.Checker, proto utils.IPProto) CheckMethod { checker = NewUDPChecker("", "", 0) case lb.CheckerPING: checker = NewPingChecker() - case lb.CheckerUDPPing: + case lb.CheckerUDPPING: checker = NewUDPPingChecker("", "", 0) + case lb.CheckerHTTP: + checker = NewHttpChecker("", "", "", 0) case lb.CheckerNone: if s.config.LbAutoMethod { switch proto { @@ -94,12 +98,12 @@ func (s *Server) NewChecker(typ lb.Checker, proto utils.IPProto) CheckMethod { } // getHealthchecks attempts to get the current healthcheck configurations from DPVS -func (s *Server) getHealthchecks() (*Checkers, error) { +func (s *Server) getHealthchecks() (*CheckerConfigs, error) { vss, err := s.comm.ListVirtualServices() if err != nil { return nil, err } - results := &Checkers{Configs: make(map[Id]*CheckerConfig)} + results := &CheckerConfigs{Configs: make(map[Id]*CheckerConfig)} for _, vs := range vss { for _, rs := range vs.RSs { target := &Target{rs.IP, rs.Port, vs.Protocol} @@ -111,13 +115,16 @@ func (s *Server) getHealthchecks() (*Checkers, error) { } weight := rs.Weight state := StateUnknown - if weight > 0 && rs.Inhibited == false { - state = StateHealthy - } else if weight == 0 && rs.Inhibited == true { + // Backend can be down adminstratively, so its weight + // should not be considered for health state. + if rs.Inhibited { state = StateUnhealthy + } else { + state = StateHealthy } // TODO: allow users to specify check interval, timeout and retry - config := NewCheckerConfig(id, checker, + config := NewCheckerConfig(id, + vs.Version, checker, target, state, weight, DefaultCheckConfig.Interval, DefaultCheckConfig.Timeout, @@ -151,17 +158,17 @@ func (s *Server) updater() { // notifier batches healthcheck notifications and sends them to DPVS. func (s *Server) notifier() { - // TODO: support more concurrency and rate limit + // TODO: support a lot more concurrences and rate limit for { select { case notification := <-s.notify: log.Infof("Sending notification >>> %v", notification) - //fmt.Println("Sending notification >>>", notification) inhibited := false if notification.Status.State == StateUnhealthy { inhibited = true } vs := &lb.VirtualService{ + Version: notification.Status.Version, Id: notification.Id.Vs(), Protocol: notification.Target.Proto, RSs: []lb.RealServer{{ @@ -172,9 +179,35 @@ func (s *Server) notifier() { }}, } - if err := s.comm.UpdateByChecker([]lb.VirtualService{*vs}); err != nil { + if changed, err := s.comm.UpdateByChecker(vs); err != nil { log.Warningf("Failed to Update %v healthy status to %v(weight: %d): %v", notification.Id, notification.State, notification.Status.Weight, err) + } else if changed != nil { + for _, rs := range changed.RSs { + version := changed.Version + id := notification.Id + target := &Target{rs.IP, rs.Port, vs.Protocol} + if !target.Equal(id.Rs()) { + continue + } + weight := rs.Weight + state := StateUnknown + if rs.Inhibited { + state = StateUnhealthy + } else { + state = StateHealthy + } + log.Warningf("%v::%s has changed, resync config %v ...", + notification.Id, notification.Target, rs) + config := NewCheckerConfig(&id, version, nil, target, state, weight, 0, 0, 0) + s.resync <- config + break + } + } else { + // resync checker config to stop repeated notificaitons + config := NewCheckerConfig(¬ification.Id, notification.Version, nil, + ¬ification.Target, notification.State, notification.Weight, 0, 0, 0) + s.resync <- config } } } @@ -186,10 +219,9 @@ func (s *Server) notifier() { // the current configurations to each of the running healthchecks. func (s *Server) manager() { notifyTicker := time.NewTicker(s.config.NotifyInterval) - var configs map[Id]*CheckerConfig for { select { - case configs = <-s.configs: + case configs := <-s.configs: // Remove healthchecks that have been deleted. for id, hc := range s.healthchecks { @@ -205,8 +237,8 @@ func (s *Server) manager() { hc := NewChecker(s.notify, conf.State, conf.Weight) hc.SetDryrun(s.config.DryRun) s.healthchecks[id] = hc - checkTicker := time.NewTicker(time.Duration((1 + rand.Intn( - int(DefaultCheckConfig.Interval.Milliseconds())))) * time.Millisecond) + checkTicker := time.NewTicker(time.Duration(1+rand.Intn(int( + DefaultCheckConfig.Interval.Milliseconds()))) * time.Millisecond) go hc.Run(checkTicker.C) } } @@ -215,14 +247,13 @@ func (s *Server) manager() { for id, hc := range s.healthchecks { hc.Update(configs[id]) } - case <-notifyTicker.C: log.Infof("Total checkers: %d", len(s.healthchecks)) - // Send notifications when status changed. - for id, hc := range s.healthchecks { + // Send notifications periodically when status in checker doesn't match config. + // It should get here only when the notification had failed. + for _, hc := range s.healthchecks { notification := hc.Notification() - if configs[id].State != notification.State { - // FIXME: Don't resend the notification after a successful one. + if hc.State != notification.State { hc.notify <- notification } } @@ -230,12 +261,25 @@ func (s *Server) manager() { } } +func (s *Server) resyncer() { + for { + select { + case conf := <-s.resync: + hc := s.healthchecks[conf.Id] + if hc != nil { + hc.Update(conf) + } + } + } +} + // Run runs a healthcheck server. func (s *Server) Run() { log.Infof("Starting healthcheck server (%v) ...", s.config) go s.updater() go s.notifier() go s.manager() + go s.resyncer() <-s.quit } diff --git a/tools/healthcheck/pkg/helthcheck/types.go b/tools/healthcheck/pkg/helthcheck/types.go index 560ebc84e..a45578836 100644 --- a/tools/healthcheck/pkg/helthcheck/types.go +++ b/tools/healthcheck/pkg/helthcheck/types.go @@ -56,29 +56,6 @@ func (id Id) Rs() *Target { return NewTargetFromStr(strId[idx+1:]) } -// MethodType is the type of check method supported for now. -type MethodType int - -const ( - MethodTypeNone MethodType = iota - MethodTypeTCP - MethodTypeUDP - MethodTypePING -) - -// String returns the name for the given MethodType. -func (h MethodType) String() string { - switch h { - case MethodTypeTCP: - return "TCP" - case MethodTypeUDP: - return "UDP" - case MethodTypePING: - return "PING" - } - return "(unknown)" -} - // CheckMethod is the interface that must be implemented by a healthcheck. type CheckMethod interface { Check(target Target, timeout time.Duration) *Result @@ -123,11 +100,11 @@ func NewTargetFromStr(str string) *Target { if idx1 < 0 || idx2 < 0 || idx1 >= idx2 { return nil } - port, err := strconv.ParseUint(str[idx2:], 10, 16) + port, err := strconv.ParseUint(str[idx2+1:], 10, 16) if err != nil { return nil } - proto := utils.IPProtoFromStr(str[idx1:idx2]) + proto := utils.IPProtoFromStr(str[idx1+1 : idx2]) if proto == 0 { return nil } @@ -146,6 +123,16 @@ func (t Target) String() string { return fmt.Sprintf("[%v]:%v:%d", t.IP, t.Proto, t.Port) } +func (t *Target) Equal(t2 *Target) bool { + if t2 == nil { + return false + } + if t.Port != t2.Port || t.Proto != t2.Proto { + return false + } + return t.IP.Equal(t2.IP) +} + // Addr returns the IP:Port representation of a healthcheck target func (t Target) Addr() string { if t.IP.To4() != nil { @@ -201,6 +188,7 @@ func NewResult(start time.Time, msg string, success bool, err error) *Result { // Status represents the current status of a healthcheck instance. type Status struct { + Version uint64 // the vs version LastCheck time.Time Duration time.Duration Failures uint64 @@ -219,7 +207,7 @@ type Notification struct { // String returns the string representation for the given notification. func (n *Notification) String() string { - return fmt.Sprintf("ID %v, %v, Weight %d, Fail %v, Success %v, Last check %s in %v", n.Id, - stateNames[n.Status.State], n.Status.Weight, n.Status.Failures, n.Status.Successes, - n.Status.LastCheck.Format("2006-01-02 15:04:05.000"), n.Status.Duration) + return fmt.Sprintf("ID %v, Version %d, %v, Weight %d, Fail %v, Success %v, Last check %s in %v", + n.Id, n.Version, stateNames[n.Status.State], n.Status.Weight, n.Status.Failures, + n.Status.Successes, n.Status.LastCheck.Format("2006-01-02 15:04:05.000"), n.Status.Duration) } diff --git a/tools/healthcheck/pkg/helthcheck/udp_ping_checker_test.go b/tools/healthcheck/pkg/helthcheck/udp_ping_checker_test.go index 321035336..a93bb356f 100644 --- a/tools/healthcheck/pkg/helthcheck/udp_ping_checker_test.go +++ b/tools/healthcheck/pkg/helthcheck/udp_ping_checker_test.go @@ -39,7 +39,7 @@ func TestUDPPingChecker(t *testing.T) { for _, target := range udpping_targets { checker := NewUDPPingChecker("", "", 0) id := Id(target.String()) - config := NewCheckerConfig(&id, checker, + config := NewCheckerConfig(&id, 0, checker, &target, StateUnknown, 0, 3*time.Second, 2*time.Second, 3) result := checker.Check(target, config.Timeout) diff --git a/tools/healthcheck/pkg/lb/dpvs_agent.go b/tools/healthcheck/pkg/lb/dpvs_agent.go index 29d403430..6634b14be 100644 --- a/tools/healthcheck/pkg/lb/dpvs_agent.go +++ b/tools/healthcheck/pkg/lb/dpvs_agent.go @@ -21,6 +21,7 @@ import ( "io" "net" "net/http" + "strconv" "strings" "time" @@ -32,7 +33,7 @@ var _ Comm = (*DpvsAgentComm)(nil) var ( serverDefault = "localhost:53225" listUri = LbApi{"/v2/vs", http.MethodGet} - noticeUri = LbApi{"/v2/vs/%s/rs?healthcheck=true", http.MethodPut} + noticeUri = LbApi{"/v2/vs/%s/rs/health?version=%d", http.MethodPut} client *http.Client = &http.Client{Timeout: httpClientTimeout} ) @@ -53,7 +54,7 @@ type DpvsAgentRs struct { IP string `json:"ip"` Port uint16 `json:"port"` Weight uint16 `json:"weight"` - Inhibited bool `json:"inhibited,omitempty"` + Inhibited *bool `json:"inhibited,omitempty"` } type DpvsAgentRsItem struct { @@ -68,7 +69,9 @@ type DpvsAgentRsListPut struct { Items []DpvsAgentRs } +// refer to `tools/dpvs-agent/models/virtual_server_spec_expand.go: VirtualServerSpecExpand` type DpvsAgentVs struct { + Version string Addr string Port uint16 Proto uint16 @@ -86,6 +89,10 @@ func (avs *DpvsAgentVs) serviceId() string { } func (avs *DpvsAgentVs) toVs() (*VirtualService, error) { + version, err := strconv.ParseUint(avs.Version, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid Vs Version %q", avs.Version) + } vip := net.ParseIP(avs.Addr) if vip == nil { return nil, fmt.Errorf("invalid Vs Addr %q", avs.Addr) @@ -105,9 +112,14 @@ func (avs *DpvsAgentVs) toVs() (*VirtualService, error) { checker = CheckerUDP case "ping": checker = CheckerPING + case "udpping": + checker = CheckerUDPPING + case "http": + checker = CheckerHTTP } } vs := &VirtualService{ + Version: version, Checker: checker, IP: vip, Port: vport, @@ -115,19 +127,10 @@ func (avs *DpvsAgentVs) toVs() (*VirtualService, error) { RSs: make([]RealServer, len(avs.Rss.Items)), } vs.Id = avs.serviceId() - - for i, ars := range avs.Rss.Items { - rip := net.ParseIP(ars.Spec.IP) - if rip == nil { - return nil, fmt.Errorf("%s: invalid Rs IP %q", vs.Id, ars.Spec.IP) - } - rs := &RealServer{ - IP: rip, - Port: ars.Spec.Port, - Weight: ars.Spec.Weight, - Inhibited: ars.Spec.Inhibited, - } - vs.RSs[i] = *rs + if rss, err := avs.Rss.toRsList(); err != nil { + return nil, fmt.Errorf("%s: %v", vs.Id, err) + } else { + vs.RSs = rss } return vs, nil } @@ -147,6 +150,26 @@ func (avslist *DpvsAgentVsList) toVsList() ([]VirtualService, error) { return vslist, nil } +func (arsl *DpvsAgentRsList) toRsList() ([]RealServer, error) { + rss := make([]RealServer, len(arsl.Items)) + for i, ars := range arsl.Items { + rip := net.ParseIP(ars.Spec.IP) + if rip == nil { + return nil, fmt.Errorf("invalid RS IP %q", ars.Spec.IP) + } + rs := &RealServer{ + IP: rip, + Port: ars.Spec.Port, + Weight: ars.Spec.Weight, + } + if ars.Spec.Inhibited != nil { + rs.Inhibited = *ars.Spec.Inhibited + } + rss[i] = *rs + } + return rss, nil +} + func NewDpvsAgentComm(server string) *DpvsAgentComm { if len(server) == 0 { server = serverDefault @@ -189,40 +212,49 @@ func (comm *DpvsAgentComm) ListVirtualServices() ([]VirtualService, error) { return vslist, nil } -func (comm *DpvsAgentComm) UpdateByChecker(targets []VirtualService) error { - // TODO: support batch operation - for _, vs := range targets { - for _, rs := range vs.RSs { - ars := &DpvsAgentRsListPut{ - Items: []DpvsAgentRs{ - { - IP: rs.IP.String(), - Port: rs.Port, - Weight: rs.Weight, - Inhibited: rs.Inhibited, - }, +func (comm *DpvsAgentComm) UpdateByChecker(vs *VirtualService) (*VirtualService, error) { + for _, rs := range vs.RSs { + ars := &DpvsAgentRsListPut{ + Items: []DpvsAgentRs{ + { + IP: rs.IP.String(), + Port: rs.Port, + Weight: rs.Weight, + Inhibited: &rs.Inhibited, }, - } - data, err := json.Marshal(ars) + }, + } + data, err := json.Marshal(ars) + if err != nil { + return nil, err + } + for _, notice := range comm.noticeApis { + url := fmt.Sprintf(notice.Url, vs.Id, vs.Version) + req, err := http.NewRequest(notice.HttpMethod, url, bytes.NewBuffer(data)) + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) if err != nil { - return err + return nil, err } - for _, notice := range comm.noticeApis { - url := fmt.Sprintf(notice.Url, vs.Id) - req, err := http.NewRequest(notice.HttpMethod, url, bytes.NewBuffer(data)) - req.Header.Set("Content-Type", "application/json") - resp, err := client.Do(req) - if err != nil { - return err + //fmt.Printf("Request: %v, Code: %v\n", req, resp.Status) + if resp.StatusCode != 200 { + if data, err = io.ReadAll(resp.Body); err != nil { + return nil, fmt.Errorf("CODE: %v", resp.StatusCode) + } + var vs DpvsAgentVs + if err = json.Unmarshal(data, &vs); err != nil { + return nil, fmt.Errorf("CODE: %v, ERROR: %s", resp.StatusCode, + strings.TrimSpace(string(data))) } - //fmt.Println("Code:", resp.Status) - if resp.StatusCode != 200 { - data, _ = io.ReadAll(resp.Body) - return fmt.Errorf("CODE: %v, ERROR: %s", resp.StatusCode, strings.TrimSpace(string(data))) + ret, err := vs.toVs() + if err != nil { + //fmt.Println("Data:", data, "len(RSs): ", len(rss.Items), "RSs:", rss) + return nil, fmt.Errorf("CODE: %v, Error: %v", resp.StatusCode, err) } - resp.Body.Close() + return ret, nil } + resp.Body.Close() } } - return nil + return nil, nil } diff --git a/tools/healthcheck/pkg/lb/dpvs_agent_test.go b/tools/healthcheck/pkg/lb/dpvs_agent_test.go index 2d8e00122..1379b9483 100644 --- a/tools/healthcheck/pkg/lb/dpvs_agent_test.go +++ b/tools/healthcheck/pkg/lb/dpvs_agent_test.go @@ -26,24 +26,22 @@ func TestListAndUpdate(t *testing.T) { t.Errorf("list error: %v", err) } t.Logf("list Results: %v", vss) - if len(vss) < 2 { + if len(vss) < 1 { return } - t.Logf("Updating %v", vss[1]) - vss[1].RSs[0].Weight = 0 - vss[1].RSs[0].Inhibited = true - //vss[1].RSs[0].Port = 8081 - //vss[1].RSs[1].Weight = 100 - //vss[1].RSs[1].Inhibited = false - //vss[1].RSs[1].IP = net.ParseIP("1.2.3.4") - if err = comm.UpdateByChecker(vss[1:2]); err != nil { + t.Logf("Updating %v", vss[0]) + vss[0].RSs[0].Weight = 0 + vss[0].RSs[0].Inhibited = true + //vss[0].RSs[0].Port = 8081 + //vss[0].RSs[0].IP = net.ParseIP("1.2.3.4") + if _, err = comm.UpdateByChecker(&vss[0]); err != nil { t.Errorf("inhibit rs error: %v", err) } time.Sleep(3 * time.Second) - t.Logf("Restoring %v", vss[1]) - vss[1].RSs[0].Weight = 100 - vss[1].RSs[0].Inhibited = false - if err = comm.UpdateByChecker(vss[1:2]); err != nil { + t.Logf("Restoring %v", vss[0]) + vss[0].RSs[0].Weight = 100 + vss[0].RSs[0].Inhibited = false + if _, err = comm.UpdateByChecker(&vss[0]); err != nil { t.Errorf("restore rs error: %v", err) } } diff --git a/tools/healthcheck/pkg/lb/types.go b/tools/healthcheck/pkg/lb/types.go index 092f4fb22..1becf5e89 100644 --- a/tools/healthcheck/pkg/lb/types.go +++ b/tools/healthcheck/pkg/lb/types.go @@ -27,7 +27,8 @@ const ( CheckerTCP CheckerUDP CheckerPING - CheckerUDPPing + CheckerUDPPING + CheckerHTTP ) type RealServer struct { @@ -39,6 +40,7 @@ type RealServer struct { type VirtualService struct { Id string + Version uint64 Checker Checker Protocol utils.IPProto Port uint16 @@ -47,8 +49,11 @@ type VirtualService struct { } type Comm interface { + // Get the list of VS/RS prepared for healthcheck. ListVirtualServices() ([]VirtualService, error) - UpdateByChecker(targets []VirtualService) error + // Update RSs health state, return nil error and the lastest info of RSs whose + // weight have been changed administively on success, or error on failure. + UpdateByChecker(targets *VirtualService) (*VirtualService, error) } func (checker Checker) String() string { @@ -61,6 +66,10 @@ func (checker Checker) String() string { return "checker_udp" case CheckerPING: return "checker_ping" + case CheckerUDPPING: + return "checker_udpping" + case CheckerHTTP: + return "checker_http" } return "checker_unknown" } diff --git a/tools/healthcheck/test/stress-test.sh b/tools/healthcheck/test/stress-test.sh index 6ea848f85..e72f59153 100755 --- a/tools/healthcheck/test/stress-test.sh +++ b/tools/healthcheck/test/stress-test.sh @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +#dpvs_agent_server=localhost:53225 ## Step 1. echo -e "Cleaning existing services ..." @@ -23,40 +24,44 @@ now=$(date +%F.%T) echo -e "[$now] Start" ## Step 2. -echo -e "Adding test services ..." +now=$(date +%F.%T) +echo -e "[$now] Adding test services ..." rsid=5000 for i in $(seq 0 32) do for j in $(seq 1 255) do vip="192.168.${i}.${j}" - flag="-t" - #udp=$((j%2)) - #[ "$udp" -eq 1 ] && flag="-u" - #echo $vip $flag - ipvsadm -A $flag $vip:80 + #echo $vip:80 + ipvsadm -At $vip:80 + #curl -sS -X PUT "http://${dpvs_agent_server}/v2/vs/${vip}-80-tcp" -H "Content-type:application/json" -d "{\"SchedName\":\"wrr\"}" >/dev/null + ipvsadm -Pt $vip:80 -z 192.168.88.241 -F dpdk0 >/dev/null 2>&1 + #curl -X PUT "http://${dpvs_agent_server}/v2/vs/${vip}-80-tcp/laddr" -H "Content-type:application/json" -d "{\"device\":\"dpdk0\", \"addr\":\"192.168.88.241\"}" for k in $(seq 5) do seg3=$((rsid/255)) seg4=$((rsid%255)) rsid=$((rsid+1)) rip="192.168.${seg3}.${seg4}" - #echo "-> $rip" - ipvsadm -a $flag $vip:80 -r $rip:8080 -b -w 100 + #echo "-> $rip:8080" + ipvsadm -at $vip:80 -r $rip:8080 -b -w 100 + #curl -sS -X PUT "http://${dpvs_agent_server}/v2/vs/${vip}-80-tcp/rs" -H "Content-type:application/json" -d "{\"Items\":[{\"ip\":\"${rip}\", \"port\":80, \"weight\":100}]}" > /dev/null done #dpip addr add $vip/32 dev dpdk0 done done ## Step 3. +now=$(date +%F.%T) echo "" echo "****************************************" -echo -e "Start healthcheck program on your own." +echo -e "[$now] Start healthcheck program on your own." echo "****************************************" echo "" ## Step 4. -echo -e "Do Checking ..." +now=$(date +%F.%T) +echo -e "[$now] Do Checking ..." while true do now=$(date +%F.%T) diff --git a/tools/ipvsadm/ipvsadm.c b/tools/ipvsadm/ipvsadm.c index dd234691e..f84a4ef0d 100644 --- a/tools/ipvsadm/ipvsadm.c +++ b/tools/ipvsadm/ipvsadm.c @@ -329,6 +329,7 @@ enum { TAG_SORT, TAG_NO_SORT, TAG_PERSISTENCE_ENGINE, + TAG_SCTP_SERVICE, TAG_SOCKPAIR, TAG_HASH_TARGET, TAG_CPU, @@ -336,6 +337,7 @@ enum { TAG_DEST_CHECK, TAG_CONN_TIMEOUT, TAG_PROXY_PROTOCOL, + TAG_QUIC, }; /* various parsing helpers & parsing functions */ @@ -432,6 +434,8 @@ static int parse_dest_check(const char *optarg, struct dest_check_configs *conf) conf->types |= DEST_HC_TCP; } else if (!strcmp(optarg, "udp")) { conf->types |= DEST_HC_UDP; + } else if (!strcmp(optarg, "sctp")) { + conf->types |= DEST_HC_SCTP; } else if (!strcmp(optarg, "ping")) { conf->types |= DEST_HC_PING; } else if (!strcmp(optarg, "default")) { @@ -470,7 +474,7 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, int c, parse; poptContext context; char *optarg= NULL; - int intarg = NULL; + int intarg = 0; struct poptOption options_table[] = { { "add-service", 'A', POPT_ARG_NONE, NULL, 'A', NULL, NULL }, { "edit-service", 'E', POPT_ARG_NONE, NULL, 'E', NULL, NULL }, @@ -504,6 +508,8 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, NULL, NULL }, { "udp-service", 'u', POPT_ARG_STRING, &optarg, 'u', NULL, NULL }, + { "sctp-service", '\0', POPT_ARG_STRING, &optarg, + TAG_SCTP_SERVICE, NULL, NULL }, { "icmp-service", 'q', POPT_ARG_STRING, &optarg, 'q', NULL, NULL }, { "icmpv6-service", '1', POPT_ARG_STRING, &optarg, '1', @@ -562,6 +568,7 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, { "dest-check", '\0', POPT_ARG_STRING, &optarg, TAG_DEST_CHECK, NULL, NULL}, { "conn-timeout", '\0', POPT_ARG_INT, &intarg, TAG_CONN_TIMEOUT, NULL, NULL}, { "proxy-protocol", '\0', POPT_ARG_STRING, &optarg, TAG_PROXY_PROTOCOL, NULL, NULL}, + { "quic", '\0', POPT_ARG_NONE, NULL, TAG_QUIC, NULL, NULL}, { NULL, 0, 0, NULL, 0, NULL, NULL } }; @@ -668,11 +675,14 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, case 'u': case 'q': case '1': + case TAG_SCTP_SERVICE: set_option(options, OPT_SERVICE); if (c == 't') { ce->dpvs_svc.proto = IPPROTO_TCP; } else if (c == 'u') { ce->dpvs_svc.proto = IPPROTO_UDP; + } else if (c == TAG_SCTP_SERVICE) { + ce->dpvs_svc.proto = IPPROTO_SCTP; } else if (c == 'q') { ce->dpvs_svc.proto = IPPROTO_ICMP; } else if (c == '1') { /*a~Z is out. ipvsadm is really not friendly here*/ @@ -892,29 +902,37 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, { dpvs_service_compat_t dpvs_svc; set_option(options,OPT_BLKLST_ADDRESS); - parse = parse_service(optarg, - &dpvs_svc); - if (!(parse & SERVICE_ADDR)) - fail(2, "illegal blacklist address"); - - ce->dpvs_blklst.af = dpvs_svc.af; - ce->dpvs_blklst.blklst = dpvs_svc.addr; + if (!strncmp(optarg, "ipset:", strlen("ipset:"))) { + strncpy(ce->dpvs_blklst.ipset, &optarg[strlen("ipset:")], + sizeof(ce->dpvs_blklst.ipset) - 1); + } else { + parse = parse_service(optarg, &dpvs_svc); + if (parse & SERVICE_ADDR) { + ce->dpvs_blklst.af = dpvs_svc.af; + ce->dpvs_blklst.subject = dpvs_svc.addr; + } else { + fail(2, "illegal blacklist entry format, require [ IP | ipset:NAME ]"); + } + } break; - } case '2': { dpvs_service_compat_t dpvs_svc; set_option(options,OPT_WHTLST_ADDRESS); - parse = parse_service(optarg, - &dpvs_svc); - if (!(parse & SERVICE_ADDR)) - fail(2, "illegal whitelist address"); - - ce->dpvs_whtlst.af = dpvs_svc.af; - ce->dpvs_whtlst.whtlst = dpvs_svc.addr; + if (!strncmp(optarg, "ipset:", strlen("ipset:"))) { + strncpy(ce->dpvs_whtlst.ipset, &optarg[strlen("ipset:")], + sizeof(ce->dpvs_whtlst.ipset) - 1); + } else { + parse = parse_service(optarg, &dpvs_svc); + if (parse & SERVICE_ADDR) { + ce->dpvs_whtlst.af = dpvs_svc.af; + ce->dpvs_whtlst.subject = dpvs_svc.addr; + } else { + fail(2, "illegal whitelist entry format, require [ IP | ipset:NAME ]"); + } + } break; - } case 'F': set_option(options, OPT_IFNAME); @@ -968,6 +986,11 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, ce->dpvs_svc.flags = ce->dpvs_svc.flags | IP_VS_SVC_F_EXPIRE_QUIESCENT; break; } + case TAG_QUIC: + { + ce->dpvs_svc.flags = ce->dpvs_svc.flags | IP_VS_SVC_F_QUIC; + break; + } case TAG_DEST_CHECK: { if (parse_dest_check(optarg, &ce->dpvs_svc.check_conf) != 0) { @@ -1379,7 +1402,7 @@ parse_service(char *buf, dpvs_service_compat_t *dpvs_svc) /* * Get sockpair from the arguments. * sockpair := PROTO:SIP:SPORT:TIP:TPORT - * PROTO := [tcp|udp] + * PROTO := [tcp|udp|sctp] * SIP,TIP := dotted-decimal ip address or square-blacketed ip6 address * SPORT,TPORT := range(0, 65535) */ @@ -1402,6 +1425,8 @@ parse_sockpair(char *buf, ipvs_sockpair_t *sockpair) proto = IPPROTO_TCP; else if (strncmp(pos, "udp", 3) == 0) proto = IPPROTO_UDP; + else if (strncmp(pos, "sctp", 4) == 0) + proto = IPPROTO_SCTP; else return 0; @@ -1474,7 +1499,7 @@ parse_sockpair(char *buf, ipvs_sockpair_t *sockpair) /* * comma separated parameters list, all fields is used to match packets. * - * proto := tcp | udp | icmp |icmpv6 + * proto := tcp | udp | sctp | icmp |icmpv6 * src-range := RANGE * dst-range := RANGE * iif := IFNAME @@ -1510,6 +1535,8 @@ static int parse_match_snat(const char *buf, dpvs_service_compat_t *dpvs_svc) dpvs_svc->proto = IPPROTO_TCP; } else if (strcmp(val, "udp") == 0) { dpvs_svc->proto = IPPROTO_UDP; + } else if (strcmp(val, "sctp") == 0) { + dpvs_svc->proto = IPPROTO_SCTP; } else if (strcmp(val, "icmp") == 0) { dpvs_svc->proto = IPPROTO_ICMP; } else if (strcmp(val, "icmpv6") == 0) { @@ -1539,9 +1566,11 @@ static int parse_match_snat(const char *buf, dpvs_service_compat_t *dpvs_svc) dpvs_svc->af = ip_af; } } else if (strcmp(key, "iif") == 0) { - snprintf(dpvs_svc->match.iifname, sizeof(dpvs_svc->match.iifname), "%s", val); + strncpy(dpvs_svc->match.iifname, val, sizeof(dpvs_svc->match.iifname) - 1); + dpvs_svc->match.iifname[sizeof(dpvs_svc->match.iifname) - 1] = '\0'; } else if (strcmp(key, "oif") == 0) { - snprintf(dpvs_svc->match.oifname, sizeof(dpvs_svc->match.oifname), "%s", val); + strncpy(dpvs_svc->match.oifname, val, sizeof(dpvs_svc->match.oifname) - 1); + dpvs_svc->match.oifname[sizeof(dpvs_svc->match.oifname) - 1] = '\0'; } else { return -1; } @@ -1663,12 +1692,12 @@ static void usage_exit(const char *program, const int exit_status) " --add-laddr -P add local address\n" " --del-laddr -Q del local address\n" " --get-laddr -G get local address\n" - " --add-blklst -U add blacklist address\n" - " --del-blklst -V del blacklist address\n" - " --get-blklst -B get blacklist address\n" - " --add-whtlst -O add whitelist address\n" - " --del-whtlst -Y del whitelist address\n" - " --get-whtlst -W get whitelist address\n" + " --add-blklst -U add blacklist address or ipset\n" + " --del-blklst -V del blacklist address or ipset\n" + " --get-blklst -B get blacklist address or ipset\n" + " --add-whtlst -O add whitelist address or ipset\n" + " --del-whtlst -Y del whitelist address or ipset\n" + " --get-whtlst -W get whitelist address or ipset\n" " --save -S save rules to stdout\n" " --add-server -a add real server with options\n" " --edit-server -e edit real server with options\n" @@ -1685,6 +1714,7 @@ static void usage_exit(const char *program, const int exit_status) "Options:\n" " --tcp-service -t service-address service-address is host[:port]\n" " --udp-service -u service-address service-address is host[:port]\n" + " --sctp-service service-address service-address is host[:port]\n" " --icmp-service -q service-address service-address is host[:port]\n" " --icmpv6-service -1 service-address service-address is host[:port]\n" " --fwmark-service -f fwmark fwmark is an integer greater than zero\n" @@ -1728,12 +1758,13 @@ static void usage_exit(const char *program, const int exit_status) " --cpu cpu_index specifi cpu (lcore) index to show, 0 for master worker\n" " --expire-quiescent expire the quiescent connections timely whose realserver went down\n" " --dest-check CHECK_CONF config health check, inhibit scheduling to failed backends\n" - " CHECK_CONF:=disable|default(passive)|DETAIL(passive)|tcp|udp|ping, DETAIL:=UPDOWN|DOWNONLY\n" + " CHECK_CONF:=disable|default(passive)|DETAIL(passive)|tcp|udp|sctp|ping, DETAIL:=UPDOWN|DOWNONLY\n" " UPDOWN:=down_retry,up_confirm,down_wait,inhibit_min-inhibit_max, for example, the default is 1,1,3s,5-3600s\n" " DOWNONLY:=down_retry,down_wait, for example, --dest-check=1,3s\n" " --laddr -z local-ip local IP\n" - " --blklst -k blacklist-ip blacklist IP for specific service\n" - " --whtlst -2 whitelist-ip whitelist IP for specific service\n", + " --blklst -k blacklist-ip specify blacklist ip address or ipset(format: \"ipset:NAME\")\n" + " --whtlst -2 whitelist-ip specify whitelist ip address or ipset(format: \"ipset:NAME\")\n" + " --quic itef quic protocol service\n", DEF_SCHED); exit(exit_status); @@ -1786,6 +1817,8 @@ static void print_conn_entry(const ipvs_conn_entry_t *conn_entry, snprintf(proto_str, sizeof(proto_str), "%s", "tcp"); else if (conn_entry->proto == IPPROTO_UDP) snprintf(proto_str, sizeof(proto_str), "%s", "udp"); + else if (conn_entry->proto == IPPROTO_SCTP) + snprintf(proto_str, sizeof(proto_str), "%s", "sctp"); else if (conn_entry->proto == IPPROTO_ICMP) snprintf(proto_str, sizeof(proto_str), "%s", "icmp"); else if (conn_entry->proto == IPPROTO_ICMPV6) @@ -2034,6 +2067,8 @@ print_service_entry(dpvs_service_compat_t *se, unsigned int format) proto = "-t"; else if (se->proto == IPPROTO_UDP) proto = "-u"; + else if (se->proto == IPPROTO_SCTP) + proto = "--sctp-service"; else proto = "-q"; @@ -2043,6 +2078,8 @@ print_service_entry(dpvs_service_compat_t *se, unsigned int format) proto = "TCP"; else if (se->proto == IPPROTO_UDP) proto = "UDP"; + else if (se->proto == IPPROTO_SCTP) + proto = "SCTP"; else if (se->proto == IPPROTO_ICMP) proto = "ICMP"; else @@ -2063,6 +2100,8 @@ print_service_entry(dpvs_service_compat_t *se, unsigned int format) proto = "tcp"; else if (se->proto == IPPROTO_UDP) proto = "udp"; + else if (se->proto == IPPROTO_SCTP) + proto = "sctp"; else if (se->proto == IPPROTO_ICMP) proto = "icmp"; else @@ -2175,6 +2214,8 @@ print_service_entry(dpvs_service_compat_t *se, unsigned int format) printf(" pp%s", proxy_protocol_str(se->proxy_protocol)); if (se->flags & IP_VS_SVC_F_EXPIRE_QUIESCENT) printf(" expire-quiescent"); + if (se->flags & IP_VS_SVC_F_QUIC && se->proto == IPPROTO_UDP) + printf(" quic"); if (se->check_conf.types) { printf(" dest-check"); if (dest_check_passive(&se->check_conf)) { @@ -2203,6 +2244,8 @@ print_service_entry(dpvs_service_compat_t *se, unsigned int format) strcat(buf, "tcp,"); if (se->check_conf.types & DEST_HC_UDP) strcat(buf, "udp,"); + if (se->check_conf.types & DEST_HC_SCTP) + strcat(buf, "sctp,"); if (se->check_conf.types & DEST_HC_PING) strcat(buf, "ping,"); *strrchr(buf, ',') = '\0'; @@ -2390,17 +2433,16 @@ static int list_all_laddrs(lcoreid_t index) static void list_blklsts_print_title(void) { - printf("%-20s %-8s %-20s\n", - "VIP:VPORT" , + printf("%-8s %-30s %-30s\n", "PROTO" , + "VIP:VPORT" , "BLACKLIST"); } -static void print_service_and_blklsts(struct dp_vs_blklst_conf *blklst) +static void print_service_and_blklsts(const struct dp_vs_blklst_conf *blklst) { - char vip[64], bip[64], port[8], proto[8]; - const char *pattern = (blklst->af == AF_INET ? - "%s:%-8s %-8s %-20s\n" : "[%s]:%-8s %-8s %-20s\n"); + char subject[64], vip[64], vport[8], proto[8], vip_port[64]; + const char *pattern = "%-8s %-30s %-30s\n"; switch (blklst->proto) { case IPPROTO_TCP: @@ -2409,6 +2451,9 @@ static void print_service_and_blklsts(struct dp_vs_blklst_conf *blklst) case IPPROTO_UDP: snprintf(proto, sizeof(proto), "%s", "UDP"); break; + case IPPROTO_SCTP: + snprintf(proto, sizeof(proto), "%s", "SCTP"); + break; case IPPROTO_ICMP: snprintf(proto, sizeof(proto), "%s", "ICMP"); break; @@ -2419,10 +2464,19 @@ static void print_service_and_blklsts(struct dp_vs_blklst_conf *blklst) break; } - snprintf(port, sizeof(port), "%u", ntohs(blklst->vport)); + snprintf(vport, sizeof(vport), "%u", ntohs(blklst->vport)); + inet_ntop(blklst->af, (const void *)&blklst->vaddr, vip, sizeof(vip)); + if (blklst->af == AF_INET6) + snprintf(vip_port, sizeof(vip_port), "[%s]:%s", vip, vport); + else + snprintf(vip_port, sizeof(vip_port), "%s:%s", vip, vport); + + if (blklst->ipset[0] == '\0') + inet_ntop(blklst->af, (const void *)&blklst->subject, subject, sizeof(subject)); + else + snprintf(subject, sizeof(subject), "ipset:%s", blklst->ipset); - printf(pattern, inet_ntop(blklst->af, (const void *)&blklst->vaddr, vip, sizeof(vip)), - port, proto, inet_ntop(blklst->af, (const void *)&blklst->blklst, bip, sizeof(bip))); + printf(pattern, proto, vip_port, subject); } static bool inet_addr_equal(int af, const union inet_addr *a1, const union inet_addr *a2) @@ -2437,9 +2491,20 @@ static bool inet_addr_equal(int af, const union inet_addr *a1, const union inet_ } } -static int list_blklst(int af, const union inet_addr *addr, uint16_t port, uint16_t protocol) +static inline void __list_blklst(int af, const union inet_addr *addr, uint16_t port, + uint16_t protocol, const struct dp_vs_blklst_conf_array *cfarr) { int i; + for (i = 0; i < cfarr->naddr; i++) { + if (inet_addr_equal(af, addr, (const union inet_addr *) &cfarr->blklsts[i].vaddr) && + port == cfarr->blklsts[i].vport && protocol == cfarr->blklsts[i].proto) { + print_service_and_blklsts(&cfarr->blklsts[i]); + } + } +} + +static int list_blklst(int af, const union inet_addr *addr, uint16_t port, uint16_t protocol) +{ struct dp_vs_blklst_conf_array *get; if (!(get = dpvs_get_blklsts())) { @@ -2447,21 +2512,17 @@ static int list_blklst(int af, const union inet_addr *addr, uint16_t port, uint1 return -1; } - for (i = 0; i < get->naddr; i++) { - if (inet_addr_equal(af, addr,(const union inet_addr *) &get->blklsts[i].vaddr) && - port == get->blklsts[i].vport && protocol == get->blklsts[i].proto) { - print_service_and_blklsts(&get->blklsts[i]); - } - } - free(get); + __list_blklst(af, addr, port, protocol, get); + free(get); return 0; } static int list_all_blklsts(void) { int i; - dpvs_services_front_t* table; + dpvs_services_front_t *table; + struct dp_vs_blklst_conf_array *barray; table = (dpvs_services_front_t*)malloc(sizeof(dpvs_services_front_t)+sizeof(dpvs_service_compat_t)*g_ipvs_info.num_services); if (!table) { @@ -2477,12 +2538,18 @@ static int list_all_blklsts(void) exit(1); } + if(!(barray = dpvs_get_blklsts())) { + fprintf(stderr, "%s\n", ipvs_strerror(errno)); + exit(1); + } + list_blklsts_print_title(); for (i = 0; i < table->count; i++) { - list_blklst(table->entrytable[i].af, &table->entrytable[i].addr, - table->entrytable[i].port, table->entrytable[i].proto); + __list_blklst(table->entrytable[i].af, &table->entrytable[i].addr, + table->entrytable[i].port, table->entrytable[i].proto, barray); } + free(barray); free(table); return 0; @@ -2490,17 +2557,16 @@ static int list_all_blklsts(void) static void list_whtlsts_print_title(void) { - printf("%-20s %-8s %-20s\n" , - "VIP:VPORT" , + printf("%-8s %-30s %-30s\n" , "PROTO" , + "VIP:VPORT" , "WHITELIST"); } -static void print_service_and_whtlsts(struct dp_vs_whtlst_conf *whtlst) +static void print_service_and_whtlsts(const struct dp_vs_whtlst_conf *whtlst) { - char vip[64], bip[64], port[8], proto[8]; - const char *pattern = (whtlst->af == AF_INET ? - "%s:%-8s %-8s %-20s\n" : "[%s]:%-8s %-8s %-20s\n"); + char subject[64], vip[64], vport[8], proto[8], vip_port[64]; + const char *pattern = "%-8s %-30s %-30s\n"; switch (whtlst->proto) { case IPPROTO_TCP: @@ -2509,6 +2575,9 @@ static void print_service_and_whtlsts(struct dp_vs_whtlst_conf *whtlst) case IPPROTO_UDP: snprintf(proto, sizeof(proto), "%s", "UDP"); break; + case IPPROTO_SCTP: + snprintf(proto, sizeof(proto), "%s", "SCTP"); + break; case IPPROTO_ICMP: snprintf(proto, sizeof(proto), "%s", "ICMP"); break; @@ -2519,15 +2588,35 @@ static void print_service_and_whtlsts(struct dp_vs_whtlst_conf *whtlst) break; } - snprintf(port, sizeof(port), "%u", ntohs(whtlst->vport)); + snprintf(vport, sizeof(vport), "%u", ntohs(whtlst->vport)); + inet_ntop(whtlst->af, (const void *)&whtlst->vaddr, vip, sizeof(vip)); + if (whtlst->af == AF_INET6) + snprintf(vip_port, sizeof(vip_port), "[%s]:%s", vip, vport); + else + snprintf(vip_port, sizeof(vip_port), "%s:%s", vip, vport); + + if (whtlst->ipset[0] == '\0') + inet_ntop(whtlst->af, (const void *)&whtlst->subject, subject, sizeof(subject)); + else + snprintf(subject, sizeof(subject), "ipset:%s", whtlst->ipset); - printf(pattern, inet_ntop(whtlst->af, (const void *)&whtlst->vaddr, vip, sizeof(vip)), - port, proto, inet_ntop(whtlst->af, (const void *)&whtlst->whtlst, bip, sizeof(bip))); + printf(pattern, proto, vip_port, subject); } -static int list_whtlst(int af, const union inet_addr *addr, uint16_t port, uint16_t protocol) +static inline void __list_whtlst(int af, const union inet_addr *addr, uint16_t port, + uint16_t protocol, const struct dp_vs_whtlst_conf_array *cfarr) { int i; + for (i = 0; i < cfarr->naddr; i++) { + if (inet_addr_equal(af, addr,(const union inet_addr *) &cfarr->whtlsts[i].vaddr) && + port == cfarr->whtlsts[i].vport && protocol == cfarr->whtlsts[i].proto) { + print_service_and_whtlsts(&cfarr->whtlsts[i]); + } + } +} + +static int list_whtlst(int af, const union inet_addr *addr, uint16_t port, uint16_t protocol) +{ struct dp_vs_whtlst_conf_array *get; if (!(get = dpvs_get_whtlsts())) { @@ -2535,22 +2624,17 @@ static int list_whtlst(int af, const union inet_addr *addr, uint16_t port, uint1 return -1; } - for (i = 0; i < get->naddr; i++) { - if (inet_addr_equal(af, addr,(const union inet_addr *) &get->whtlsts[i].vaddr) && - port == get->whtlsts[i].vport && protocol == get->whtlsts[i].proto) { - print_service_and_whtlsts(&get->whtlsts[i]); - } - } + __list_whtlst(af, addr, port, protocol, get); free(get); - return 0; } static int list_all_whtlsts(void) { - dpvs_services_front_t* table; int i; + dpvs_services_front_t *table; + struct dp_vs_whtlst_conf_array *warray; table = (dpvs_services_front_t*)malloc(sizeof(dpvs_services_front_t)+sizeof(dpvs_service_compat_t)*g_ipvs_info.num_services); if (!table) { @@ -2566,12 +2650,18 @@ static int list_all_whtlsts(void) exit(1); } + if (!(warray = dpvs_get_whtlsts())) { + fprintf(stderr, "%s\n", ipvs_strerror(errno)); + exit(1); + } + list_whtlsts_print_title(); for (i = 0; i < table->count; i++) { - list_whtlst(table->entrytable[i].af, &table->entrytable[i].addr, - table->entrytable[i].port, table->entrytable[i].proto); + __list_whtlst(table->entrytable[i].af, &table->entrytable[i].addr, + table->entrytable[i].port, table->entrytable[i].proto, warray); } + free(warray); free(table); return 0; @@ -2706,6 +2796,9 @@ int service_to_port(const char *name, unsigned short proto) else if (proto == IPPROTO_UDP && (service = getservbyname(name, "udp")) != NULL) return ntohs((unsigned short) service->s_port); + else if (proto == IPPROTO_SCTP + && (service = getservbyname(name, "sctp")) != NULL) + return ntohs((unsigned short) service->s_port); else if (proto == IPPROTO_ICMP && (service = getservbyname(name, "icmp")) != NULL) return ntohs((unsigned short) service->s_port); @@ -2727,6 +2820,9 @@ static char * port_to_service(unsigned short port, unsigned short proto) else if (proto == IPPROTO_UDP && (service = getservbyport(htons(port), "udp")) != NULL) return service->s_name; + else if (proto == IPPROTO_SCTP && + (service = getservbyport(htons(port), "sctp")) != NULL) + return service->s_name; else if (proto == IPPROTO_ICMP && (service = getservbyport(htons(port), "icmp")) != NULL) return service->s_name; diff --git a/tools/keepalived/keepalived/check/check_data.c b/tools/keepalived/keepalived/check/check_data.c index ba3ece87e..679a7653a 100644 --- a/tools/keepalived/keepalived/check/check_data.c +++ b/tools/keepalived/keepalived/check/check_data.c @@ -106,8 +106,10 @@ free_whtlst_group(void *data) FREE_PTR(whtlst_group->gname); free_list(&whtlst_group->addr_ip); free_list(&whtlst_group->range); + free_list(&whtlst_group->ipset); FREE(whtlst_group); } + static void dump_whtlst_group(FILE *fp, const void *data) { @@ -116,18 +118,23 @@ dump_whtlst_group(FILE *fp, const void *data) conf_write(fp, " whitelist IP address group = %s", whtlst_group->gname); dump_list(fp, whtlst_group->addr_ip); dump_list(fp, whtlst_group->range); + dump_list(fp, whtlst_group->ipset); } + static void free_whtlst_entry(void *data) { FREE(data); } + static void dump_whtlst_entry(FILE *fp, const void *data) { const whtlst_addr_entry *whtlst_entry = data; - if (whtlst_entry->range) + if (!strncmp(whtlst_entry->ipset, "ipset:", sizeof("ipset:") - 1)) + conf_write(fp, " IPSET = %s", whtlst_entry->ipset); + else if (whtlst_entry->range) conf_write(fp, " IP Range = %s-%d" , inet_sockaddrtos(&whtlst_entry->addr) , whtlst_entry->range); @@ -135,6 +142,7 @@ dump_whtlst_entry(FILE *fp, const void *data) conf_write(fp, " IP = %s" , inet_sockaddrtos(&whtlst_entry->addr)); } + void alloc_whtlst_group(char *gname) { @@ -146,21 +154,33 @@ alloc_whtlst_group(char *gname) memcpy(new->gname, gname, size); new->addr_ip = alloc_list(free_whtlst_entry, dump_whtlst_entry); new->range = alloc_list(free_whtlst_entry, dump_whtlst_entry); + new->ipset = alloc_list(free_whtlst_entry, dump_whtlst_entry); list_add(check_data->whtlst_group, new); } + void alloc_whtlst_entry(const vector_t *strvec) { whtlst_addr_group *whtlst_group = LIST_TAIL_DATA(check_data->whtlst_group); whtlst_addr_entry *new; + const char *str_entry; new = (whtlst_addr_entry *) MALLOC(sizeof (whtlst_addr_entry)); + if (!new) + return; + str_entry = strvec_slot(strvec, 0); + + if (!strncmp(str_entry, "ipset:", sizeof("ipset:") - 1)) { + strncpy(new->ipset, &str_entry[sizeof("ipset:")-1], sizeof(new->ipset) - 1); + list_add(whtlst_group->ipset, new); + return; + } - inet_stor(vector_slot(strvec, 0), &new->range); + inet_stor(str_entry, &new->range); if (new->range == UINT32_MAX) new->range = 0; - inet_stosockaddr(vector_slot(strvec, 0), NULL, &new->addr); + inet_stosockaddr(str_entry, NULL, &new->addr); if (!new->range) list_add(whtlst_group->addr_ip, new); @@ -257,7 +277,6 @@ alloc_vsg_entry(const vector_t *strvec) unsigned fwmark; new = (virtual_server_group_entry_t *) MALLOC(sizeof(virtual_server_group_entry_t)); - if (!strcmp(strvec_slot(strvec, 0), "fwmark")) { if (!read_unsigned_strvec(strvec, 1, &fwmark, 0, UINT32_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): fwmark '%s' must be in [0, %u] - ignoring", vsg->gname, strvec_slot(strvec, 1), UINT32_MAX); @@ -508,11 +527,15 @@ dump_vs(FILE *fp, const void *data) if (vs->blklst_addr_gname) conf_write(fp, " BLACK_LIST GROUP = %s", vs->blklst_addr_gname); + if (vs->whtlst_addr_gname) + conf_write(fp, " WHITE_LIST GROUP = %s", vs->whtlst_addr_gname); + if (vs->vip_bind_dev) - conf_write(fp, " vip_bind_dev = %s", vs->blklst_addr_gname); + conf_write(fp, " vip_bind_dev = %s", vs->vip_bind_dev); conf_write(fp, " SYN proxy is %s", vs->syn_proxy ? "ON" : "OFF"); conf_write(fp, " expire_quiescent_conn is %s", vs->expire_quiescent_conn ? "ON" : "OFF"); + conf_write(fp, " quic is %s", vs->quic ? "ON" : "OFF"); if (vs->hash_target) { switch (vs->hash_target) { @@ -559,8 +582,7 @@ alloc_vs(const char *param1, const char *param2) new->vfwmark = fwmark; } else if (!strcmp(param1, "match")) { - new->forwarding_method = IP_VS_CONN_F_SNAT; - + new->forwarding_method = IP_VS_CONN_F_SNAT; } else { /* Don't pass a zero for port number to inet_stosockaddr. This was added in v2.0.7 * to support legacy configuration since previously having no port wasn't allowed. */ @@ -604,6 +626,7 @@ alloc_vs(const char *param1, const char *param2) new->conn_timeout = 0; new->syn_proxy = false; new->expire_quiescent_conn = false; + new->quic = false; new->local_addr_gname = NULL; new->blklst_addr_gname = NULL; new->whtlst_addr_gname = NULL; @@ -620,8 +643,8 @@ alloc_vs(const char *param1, const char *param2) } /*local address group facility functions*/ -static void -free_laddr_group(void *data) +static void +free_laddr_group(void *data) { local_addr_group *laddr_group = (local_addr_group*)data; FREE_PTR(laddr_group->gname); @@ -706,6 +729,7 @@ free_blklst_group(void *data) FREE_PTR(blklst_group->gname); free_list(&blklst_group->addr_ip); free_list(&blklst_group->range); + free_list(&blklst_group->ipset); FREE(blklst_group); } @@ -717,6 +741,7 @@ dump_blklst_group(FILE *fp, const void *data) conf_write(fp, " blacllist IP address group = %s", blklst_group->gname); dump_list(fp, blklst_group->addr_ip); dump_list(fp, blklst_group->range); + dump_list(fp, blklst_group->ipset); } static void @@ -730,7 +755,9 @@ dump_blklst_entry(FILE *fp, const void *data) { const blklst_addr_entry *blklst_entry = data; - if (blklst_entry->range) + if (!strncmp(blklst_entry->ipset, "ipset:", sizeof("ipset:") - 1)) + conf_write(fp, " IPSET = %s", blklst_entry->ipset); + else if (blklst_entry->range) conf_write(fp, " IP Range = %s-%d" , inet_sockaddrtos(&blklst_entry->addr) , blklst_entry->range); @@ -750,6 +777,7 @@ alloc_blklst_group(char *gname) memcpy(new->gname, gname, size); new->addr_ip = alloc_list(free_blklst_entry, dump_blklst_entry); new->range = alloc_list(free_blklst_entry, dump_blklst_entry); + new->ipset = alloc_list(free_blklst_entry, dump_blklst_entry); list_add(check_data->blklst_group, new); } @@ -759,14 +787,24 @@ alloc_blklst_entry(const vector_t *strvec) { blklst_addr_group *blklst_group = LIST_TAIL_DATA(check_data->blklst_group); blklst_addr_entry *new; + const char *str_entry; new = (blklst_addr_entry *) MALLOC(sizeof (blklst_addr_entry)); + if (!new) + return; + str_entry = strvec_slot(strvec, 0); - inet_stor(vector_slot(strvec, 0), &new->range); + if (!strncmp(str_entry, "ipset:", sizeof("ipset:") - 1)) { + strncpy(new->ipset, &str_entry[sizeof("ipset:")-1], sizeof(new->ipset) - 1); + list_add(blklst_group->ipset, new); + return; + } + + inet_stor(str_entry, &new->range); /* If no range specified, new->range == UINT32_MAX */ if (new->range == UINT32_MAX) new->range = 0; - inet_stosockaddr(vector_slot(strvec, 0), NULL, &new->addr); + inet_stosockaddr(str_entry, NULL, &new->addr); if (!new->range) list_add(blklst_group->addr_ip, new); @@ -1050,7 +1088,7 @@ alloc_check_data(void) #endif new->laddr_group = alloc_list(free_laddr_group, dump_laddr_group); new->blklst_group = alloc_list(free_blklst_group, dump_blklst_group); - new->whtlst_group = alloc_list(free_whtlst_group, dump_whtlst_group); + new->whtlst_group = alloc_list(free_whtlst_group, dump_whtlst_group); new->tunnel_group = alloc_list(free_tunnel_group, dump_tunnel_group); return new; @@ -1068,6 +1106,7 @@ free_check_data(check_data_t *data) #endif free_list(&data->laddr_group); free_list(&data->blklst_group); + free_list(&data->whtlst_group); free_list(&data->tunnel_group); FREE(data); } @@ -1087,6 +1126,8 @@ dump_check_data(FILE *fp, check_data_t *data) dump_list(fp, data->laddr_group); if (!LIST_ISEMPTY(data->blklst_group)) dump_list(fp, data->blklst_group); + if (!LIST_ISEMPTY(data->whtlst_group)) + dump_list(fp, data->whtlst_group); if (!LIST_ISEMPTY(data->vs_group)) dump_list(fp, data->vs_group); dump_list(fp, data->vs); @@ -1128,6 +1169,9 @@ char *dump_vs_match(const virtual_server_t *vs) case IPPROTO_UDP: snprintf(vs_str, sizeof(vs_str) - 1, "%s", "udp"); break; + case IPPROTO_SCTP: + snprintf(vs_str, sizeof(vs_str) - 1, "%s", "sctp"); + break; case IPPROTO_ICMP: snprintf(vs_str, sizeof(vs_str) - 1, "%s", "icmp"); break; diff --git a/tools/keepalived/keepalived/check/check_parser.c b/tools/keepalived/keepalived/check/check_parser.c index 8c4f26dcf..1511d6350 100644 --- a/tools/keepalived/keepalived/check/check_parser.c +++ b/tools/keepalived/keepalived/check/check_parser.c @@ -956,6 +956,13 @@ expire_quiescent_handler(const vector_t *strvec) vs->expire_quiescent_conn = true; } +static void +quic_handler(const vector_t *strvec) +{ + virtual_server_t *vs = LIST_TAIL_DATA(check_data->vs); + vs->quic = true; +} + static void bind_dev_handler(const vector_t *strvec) { @@ -1240,6 +1247,7 @@ init_check_keywords(bool active) install_keyword("waddr_group_name", &whtlst_gname_handler); install_keyword("syn_proxy", &syn_proxy_handler); install_keyword("expire_quiescent_conn", &expire_quiescent_handler); + install_keyword("quic", &quic_handler); install_keyword("vip_bind_dev", &bind_dev_handler); } diff --git a/tools/keepalived/keepalived/check/ipvswrapper.c b/tools/keepalived/keepalived/check/ipvswrapper.c index 61f590be9..9a1398da4 100755 --- a/tools/keepalived/keepalived/check/ipvswrapper.c +++ b/tools/keepalived/keepalived/check/ipvswrapper.c @@ -615,8 +615,8 @@ ipvs_blklst_range_cmd(int cmd, blklst_addr_entry *blklst_entry, dpvs_service_com memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); blklst_rule.af = blklst_entry->addr.ss_family; if (blklst_entry->addr.ss_family == AF_INET6) { - inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.blklst.in6); - ip = blklst_rule.blklst.in6.s6_addr32[3]; + inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.subject.in6); + ip = blklst_rule.subject.in6.s6_addr32[3]; } else { ip = inet_sockaddrip4(&blklst_entry->addr); } @@ -625,9 +625,9 @@ ipvs_blklst_range_cmd(int cmd, blklst_addr_entry *blklst_entry, dpvs_service_com ((addr_ip >> 24) & 0xFF) <= blklst_entry->range; addr_ip += 0x01000000) { if (blklst_entry->addr.ss_family == AF_INET6) - blklst_rule.blklst.in6.s6_addr32[3] = addr_ip; + blklst_rule.subject.in6.s6_addr32[3] = addr_ip; else - blklst_rule.blklst.in.s_addr = addr_ip; + blklst_rule.subject.in.s_addr = addr_ip; ipvs_talk(cmd, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); } @@ -649,9 +649,9 @@ ipvs_blklst_group_cmd(int cmd, blklst_addr_group *blklst_group, dpvs_service_com memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); blklst_rule.af = blklst_entry->addr.ss_family; if (blklst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.blklst.in6); + inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.subject.in6); else - blklst_rule.blklst.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); + blklst_rule.subject.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); ipvs_talk(cmd, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); } @@ -659,6 +659,13 @@ ipvs_blklst_group_cmd(int cmd, blklst_addr_group *blklst_group, dpvs_service_com LIST_FOREACH(l, blklst_entry, e) { ipvs_blklst_range_cmd(cmd, blklst_entry, srule); } + + l = blklst_group->ipset; + LIST_FOREACH(l, blklst_entry, e) { + memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); + strncpy(blklst_rule.ipset, blklst_entry->ipset, sizeof(blklst_rule.ipset) - 1); + ipvs_talk(cmd, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); + } } static void @@ -795,6 +802,10 @@ static void ipvs_set_srule(int cmd, dpvs_service_compat_t *srule, virtual_server srule->flags |= IP_VS_SVC_F_EXPIRE_QUIESCENT; } + if (vs->quic) { + srule->flags |= IP_VS_SVC_F_QUIC; + } + if (!strcmp(vs->sched, "conhash")) { if (vs->hash_target) { if ((srule->proto != IPPROTO_UDP) && @@ -854,8 +865,8 @@ ipvs_whtlst_range_cmd(int cmd, whtlst_addr_entry *whtlst_entry, dpvs_service_com memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); whtlst_rule.af = whtlst_entry->addr.ss_family; if (whtlst_entry->addr.ss_family == AF_INET6) { - inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.whtlst.in6); - ip = whtlst_rule.whtlst.in6.s6_addr32[3]; + inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.subject.in6); + ip = whtlst_rule.subject.in6.s6_addr32[3]; } else { ip = inet_sockaddrip4(&whtlst_entry->addr); } @@ -863,9 +874,9 @@ ipvs_whtlst_range_cmd(int cmd, whtlst_addr_entry *whtlst_entry, dpvs_service_com for (addr_ip = ip; ((addr_ip >> 24) & 0xFF) <= whtlst_entry->range; addr_ip += 0x01000000) { if (whtlst_entry->addr.ss_family == AF_INET6) - whtlst_rule.whtlst.in6.s6_addr32[3] = addr_ip; + whtlst_rule.subject.in6.s6_addr32[3] = addr_ip; else - whtlst_rule.whtlst.in.s_addr = addr_ip; + whtlst_rule.subject.in.s_addr = addr_ip; ipvs_talk(cmd, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); } @@ -887,9 +898,9 @@ ipvs_whtlst_group_cmd(int cmd, whtlst_addr_group *whtlst_group, dpvs_service_com memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); whtlst_rule.af = whtlst_entry->addr.ss_family; if (whtlst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.whtlst.in6); + inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.subject.in6); else - whtlst_rule.whtlst.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); + whtlst_rule.subject.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); ipvs_talk(cmd, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); } @@ -897,6 +908,13 @@ ipvs_whtlst_group_cmd(int cmd, whtlst_addr_group *whtlst_group, dpvs_service_com LIST_FOREACH(l, whtlst_entry, e) { ipvs_whtlst_range_cmd(cmd, whtlst_entry, srule); } + + l = whtlst_group->ipset; + LIST_FOREACH(l, whtlst_entry, e) { + memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); + strncpy(whtlst_rule.ipset, whtlst_entry->ipset, sizeof(whtlst_rule.ipset) - 1); + ipvs_talk(cmd, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); + } } static void @@ -1277,29 +1295,36 @@ ipvs_rm_bentry_from_vsg(blklst_addr_entry *blklst_entry, whtlst_addr_entry *whtl srule->addr.in.s_addr = ip; if (blklst_entry != NULL) { - if (blklst_entry->range) + if(blklst_entry->ipset[0] != '\0') { + memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); + strncpy(blklst_rule.ipset, blklst_entry->ipset, sizeof(blklst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELBLKLST, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); + } else if (blklst_entry->range) { ipvs_blklst_range_cmd(IP_VS_SO_SET_DELBLKLST, blklst_entry, srule); - else { + } else { memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); blklst_rule.af = blklst_entry->addr.ss_family; if (blklst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.blklst.in6); + inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.subject.in6); else - blklst_rule.blklst.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); + blklst_rule.subject.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELBLKLST, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); } } if (whtlst_entry != NULL) { - if (whtlst_entry->range) + if (whtlst_entry->ipset[0] != '\0') { + memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); + strncpy(whtlst_rule.ipset, whtlst_entry->ipset, sizeof(whtlst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELWHTLST, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); + } else if (whtlst_entry->range) { ipvs_whtlst_range_cmd(IP_VS_SO_SET_DELWHTLST, whtlst_entry, srule); - else { + } else { memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); whtlst_rule.af = whtlst_entry->addr.ss_family; if (whtlst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.whtlst.in6); + inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.subject.in6); else - whtlst_rule.whtlst.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); - + whtlst_rule.subject.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELWHTLST, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); } } @@ -1315,31 +1340,37 @@ ipvs_rm_bentry_from_vsg(blklst_addr_entry *blklst_entry, whtlst_addr_entry *whtl srule->addr.in.s_addr = addr_ip; if (blklst_entry != NULL) { - if (blklst_entry->range) + if(blklst_entry->ipset[0] != '\0') { + memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); + strncpy(blklst_rule.ipset, blklst_entry->ipset, sizeof(blklst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELBLKLST, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); + } else if (blklst_entry->range) { ipvs_blklst_range_cmd(IP_VS_SO_SET_DELBLKLST, blklst_entry, srule); - else { + } else { memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); blklst_rule.af = blklst_entry->addr.ss_family; if (blklst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.blklst.in6); + inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.subject.in6); else - blklst_rule.blklst.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); + blklst_rule.subject.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELBLKLST, srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); } } - if (whtlst_entry != NULL) - { - if (whtlst_entry->range) + if (whtlst_entry != NULL) { + if (whtlst_entry->ipset[0] != '\0') { + memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); + strncpy(whtlst_rule.ipset, whtlst_entry->ipset, sizeof(whtlst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELWHTLST, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); + } else if (whtlst_entry->range) { ipvs_whtlst_range_cmd(IP_VS_SO_SET_DELWHTLST, whtlst_entry, srule); - else { + } else { memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); whtlst_rule.af = whtlst_entry->addr.ss_family; if (whtlst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.whtlst.in6); + inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.subject.in6); else - whtlst_rule.whtlst.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); - + whtlst_rule.subject.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELWHTLST, srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); } } @@ -1369,15 +1400,19 @@ ipvs_blklst_remove_entry(virtual_server_t *vs, blklst_addr_entry *blklst_entry) } srule.port = inet_sockaddrport(&vs->addr); - if (blklst_entry->range) { + if(blklst_entry->ipset[0] != '\0') { + memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); + strncpy(blklst_rule.ipset, blklst_entry->ipset, sizeof(blklst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELBLKLST, &srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); + } else if (blklst_entry->range) { ipvs_blklst_range_cmd(IP_VS_SO_SET_DELBLKLST, blklst_entry, &srule); } else { memset(&blklst_rule, 0, sizeof(dpvs_blklst_t)); blklst_rule.af = blklst_entry->addr.ss_family; if (blklst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.blklst.in6); + inet_sockaddrip6(&blklst_entry->addr, &blklst_rule.subject.in6); else - blklst_rule.blklst.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); + blklst_rule.subject.in.s_addr = inet_sockaddrip4(&blklst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELBLKLST, &srule, NULL, NULL, NULL, &blklst_rule, NULL, NULL, false); } @@ -1408,15 +1443,19 @@ ipvs_whtlst_remove_entry(virtual_server_t *vs, whtlst_addr_entry *whtlst_entry) } srule.port = inet_sockaddrport(&vs->addr); - if (whtlst_entry->range) { + if (whtlst_entry->ipset[0] != '\0') { + memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); + strncpy(whtlst_rule.ipset, whtlst_entry->ipset, sizeof(whtlst_rule.ipset) - 1); + ipvs_talk(IP_VS_SO_SET_DELWHTLST, &srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); + } else if (whtlst_entry->range) { ipvs_whtlst_range_cmd(IP_VS_SO_SET_DELWHTLST, whtlst_entry, &srule); } else { memset(&whtlst_rule, 0, sizeof(dpvs_whtlst_t)); whtlst_rule.af = whtlst_entry->addr.ss_family; if (whtlst_entry->addr.ss_family == AF_INET6) - inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.whtlst.in6); + inet_sockaddrip6(&whtlst_entry->addr, &whtlst_rule.subject.in6); else - whtlst_rule.whtlst.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); + whtlst_rule.subject.in.s_addr = inet_sockaddrip4(&whtlst_entry->addr); ipvs_talk(IP_VS_SO_SET_DELWHTLST, &srule, NULL, NULL, NULL, NULL, &whtlst_rule, NULL, false); } diff --git a/tools/keepalived/keepalived/check/ipwrapper.c b/tools/keepalived/keepalived/check/ipwrapper.c index d2658c067..8b3fc470d 100755 --- a/tools/keepalived/keepalived/check/ipwrapper.c +++ b/tools/keepalived/keepalived/check/ipwrapper.c @@ -1024,6 +1024,12 @@ clear_diff_rs(virtual_server_t *old_vs, virtual_server_t *new_vs, list old_check } } clear_service_rs(old_vs, rs_to_remove, false); + + //keep new_vs quorum_state_up same with old_vs + if (old_vs->quorum_state_up != new_vs->quorum_state_up) { + new_vs->quorum_state_up = old_vs->quorum_state_up; + } + free_list(&rs_to_remove); } @@ -1165,7 +1171,8 @@ blklst_entry_exist(blklst_addr_entry *blklst_entry, list l) LIST_FOREACH(l, entry, e) { if (sockstorage_equal(&entry->addr, &blklst_entry->addr) && - entry->range == blklst_entry->range) + entry->range == blklst_entry->range && + !strncmp(entry->ipset, blklst_entry->ipset, sizeof(entry->ipset))) return 1; } return 0; @@ -1228,25 +1235,27 @@ clear_diff_blklst(virtual_server_t * old_vs, virtual_server_t * new_vs) if (!old) return 1; + if (new_vs->blklst_addr_gname) + new = ipvs_get_blklst_group_by_name(new_vs->blklst_addr_gname, + check_data->blklst_group); + /* if new_vs has no blacklist group, delete all blklst address from old_vs */ - if (!new_vs->blklst_addr_gname) { + if (!new) { if (!clear_all_blklst_entry(old->addr_ip, old_vs)) return 0; if (!clear_all_blklst_entry(old->range, old_vs)) return 0; + if (!clear_all_blklst_entry(old->ipset, old_vs)) + return 0; return 1; } - else - /* Fetch new_vs blacklist address group */ - new = ipvs_get_blklst_group_by_name(new_vs->blklst_addr_gname, - check_data->blklst_group); - if (!new) - return 1; if (!clear_diff_blklst_entry(old->addr_ip, new->addr_ip, old_vs)) return 0; if (!clear_diff_blklst_entry(old->range, new->range, old_vs)) return 0; + if (!clear_diff_blklst_entry(old->ipset, new->ipset, old_vs)) + return 0; return 1; } @@ -1261,8 +1270,9 @@ whtlst_entry_exist(whtlst_addr_entry *whtlst_entry, list l) for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { entry = ELEMENT_DATA(e); if (sockstorage_equal(&entry->addr, &whtlst_entry->addr) && - entry->range == whtlst_entry->range) - return 1; + entry->range == whtlst_entry->range && + !strncmp(entry->ipset, whtlst_entry->ipset, sizeof(entry->ipset))) + return 1; } return 0; } @@ -1321,28 +1331,30 @@ clear_diff_whtlst(virtual_server_t * old_vs, virtual_server_t * new_vs) /* Fetch whitelist address group */ old = ipvs_get_whtlst_group_by_name(old_vs->whtlst_addr_gname, old_check_data->whtlst_group); - if (!old) return 1; + + if (new_vs->whtlst_addr_gname) + new = ipvs_get_whtlst_group_by_name(new_vs->whtlst_addr_gname, + check_data->whtlst_group); + /* if new_vs has no whitelist group, delete all whtlst address from old_vs */ - if (!new_vs->whtlst_addr_gname) { + if (!new) { if (!clear_all_whtlst_entry(old->addr_ip, old_vs)) return 0; if (!clear_all_whtlst_entry(old->range, old_vs)) return 0; + if (!clear_all_whtlst_entry(old->ipset, old_vs)) + return 0; return 1; } - else - /* Fetch new_vs whitelist address group */ - new = ipvs_get_whtlst_group_by_name(new_vs->whtlst_addr_gname, - check_data->whtlst_group); - if (!new) - return 1; if (!clear_diff_whtlst_entry(old->addr_ip, new->addr_ip, old_vs)) return 0; if (!clear_diff_whtlst_entry(old->range, new->range, old_vs)) return 0; + if (!clear_diff_whtlst_entry(old->ipset, new->ipset, old_vs)) + return 0; return 1; } @@ -1394,6 +1406,7 @@ clear_diff_services(list old_checkers_queue) vs->bps != new_vs->bps || vs->limit_proportion != new_vs->limit_proportion || vs->hash_target != new_vs->hash_target || vs->syn_proxy != new_vs->syn_proxy || vs->expire_quiescent_conn != new_vs->expire_quiescent_conn || + vs->quic != new_vs->quic || strcmp(vs->srange, new_vs->srange) || strcmp(vs->drange, new_vs->drange) || strcmp(vs->iifname, new_vs->iifname) || strcmp(vs->oifname, new_vs->oifname)) { ipvs_cmd(IP_VS_SO_SET_EDIT, new_vs, NULL); diff --git a/tools/keepalived/keepalived/check/libipvs.c b/tools/keepalived/keepalived/check/libipvs.c index f81efce7a..08e41ed0f 100644 --- a/tools/keepalived/keepalived/check/libipvs.c +++ b/tools/keepalived/keepalived/check/libipvs.c @@ -293,23 +293,16 @@ int dpvs_del_laddr(dpvs_service_compat_t *svc, dpvs_laddr_table_t *laddr) dpvs_fill_ipaddr_conf(0, 0, laddr, ¶m); dpvs_setsockopt(SOCKOPT_SET_IFADDR_DEL, ¶m, sizeof(struct inet_addr_param)); - return dpvs_setsockopt(SOCKOPT_SET_LADDR_DEL, laddr, sizeof(laddr)); + return dpvs_setsockopt(SOCKOPT_SET_LADDR_DEL, laddr, sizeof(dpvs_laddr_table_t)); } /*for black list*/ static void dpvs_fill_blklst_conf(dpvs_service_compat_t *svc, dpvs_blklst_t *blklst) { - blklst->af = svc->af; - blklst->proto = svc->proto; - blklst->vport = svc->port; - blklst->fwmark = svc->fwmark; - if (svc->af == AF_INET) { - blklst->vaddr.in = svc->addr.in; - } else { - blklst->vaddr.in6 = svc->addr.in6; - } - - return; + blklst->af = svc->af; + blklst->proto = svc->proto; + blklst->vport = svc->port; + blklst->vaddr = svc->addr; } int dpvs_add_blklst(dpvs_service_compat_t* svc, dpvs_blklst_t *blklst) @@ -336,14 +329,7 @@ static void dpvs_fill_whtlst_conf(dpvs_service_compat_t *svc, dpvs_whtlst_t *wht whtlst->af = svc->af; whtlst->proto = svc->proto; whtlst->vport = svc->port; - whtlst->fwmark = svc->fwmark; - if (svc->af == AF_INET) { - whtlst->vaddr.in = svc->addr.in; - } else { - whtlst->vaddr.in6 = svc->addr.in6; - } - - return; + whtlst->vaddr = svc->addr; } int dpvs_add_whtlst(dpvs_service_compat_t* svc, dpvs_whtlst_t *whtlst) diff --git a/tools/keepalived/keepalived/include/check_data.h b/tools/keepalived/keepalived/include/check_data.h index 2d5debc66..6b32141e6 100644 --- a/tools/keepalived/keepalived/include/check_data.h +++ b/tools/keepalived/keepalived/include/check_data.h @@ -132,6 +132,7 @@ typedef struct _local_addr_group { typedef struct _blklst_addr_entry { struct sockaddr_storage addr; uint32_t range; + char ipset[IPSET_MAXNAMELEN]; } blklst_addr_entry; @@ -139,18 +140,21 @@ typedef struct _blklst_addr_group { char *gname; list addr_ip; list range; + list ipset; } blklst_addr_group; /* whitelist ip group*/ typedef struct _whtlst_addr_entry { struct sockaddr_storage addr; uint32_t range; + char ipset[IPSET_MAXNAMELEN]; } whtlst_addr_entry; typedef struct _whtlst_addr_group { char *gname; list addr_ip; list range; + list ipset; } whtlst_addr_group; typedef struct _tunnel_entry { @@ -236,6 +240,7 @@ typedef struct _virtual_server { * the service from IPVS topology. */ bool syn_proxy; bool expire_quiescent_conn; + bool quic; unsigned int connection_to; /* connection time-out */ unsigned long delay_loop; /* Interval between running checker */ unsigned long warmup; /* max random timeout to start checker */ @@ -314,6 +319,7 @@ static inline bool quorum_equal(const notify_script_t *quorum1, (X)->hash_target == (Y)->hash_target &&\ (X)->syn_proxy == (Y)->syn_proxy &&\ (X)->expire_quiescent_conn == (Y)->expire_quiescent_conn &&\ + (X)->quic == (Y)->quic &&\ quorum_equal((X)->notify_quorum_up, (Y)->notify_quorum_up) &&\ quorum_equal((X)->notify_quorum_down, (Y)->notify_quorum_down) &&\ !strcmp((X)->sched, (Y)->sched) &&\ diff --git a/tools/keepalived/keepalived/include/layer4.h b/tools/keepalived/keepalived/include/layer4.h index 6f47bb8c2..db399ef4c 100644 --- a/tools/keepalived/keepalived/include/layer4.h +++ b/tools/keepalived/keepalived/include/layer4.h @@ -55,6 +55,7 @@ typedef struct _conn_opts { /* Prototypes defs */ #ifdef _WITH_LVS_ +extern void set_buf(char *buf, size_t buf_len); extern enum connect_result socket_bind_connect(int, conn_opts_t *); #endif diff --git a/tools/keepalived/keepalived/vrrp/vrrp_daemon.c b/tools/keepalived/keepalived/vrrp/vrrp_daemon.c index ea19cc6d3..3998461d0 100644 --- a/tools/keepalived/keepalived/vrrp/vrrp_daemon.c +++ b/tools/keepalived/keepalived/vrrp/vrrp_daemon.c @@ -21,6 +21,7 @@ */ #include "config.h" +#include "sockopt.h" #ifdef _HAVE_SCHED_RT_ #include