aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesper Dangaard Brouer <brouer@redhat.com>2019-07-02 12:49:01 +0200
committerJesper Dangaard Brouer <brouer@redhat.com>2019-07-02 12:49:01 +0200
commit0c7a7fcaa12414014af9eeccf6274d9fe8315079 (patch)
tree4eb600b8b9bf2c24e303d274681da1848b5a4318
parent9a947723cc9d36c5b92ecb8d506abc923137a72b (diff)
parentc3c2c3c6ae12e316218493e0174ae8c8cf597faa (diff)
downloadipvsadm-0c7a7fcaa12414014af9eeccf6274d9fe8315079.tar.gz
Merge branch 'GUE-encap'
Jacky Hu says: ============== This patchset allows tunneling with gue encapsulation. v8->v7: 1) fixed a few style issues from scripts/checkpatch.pl --strict 2) use up to 4 letters in the comments 3) updated document for new options v7->v6: 1) fix type of local variable in parse_tun_type 2) use up to 4 letters in the comments 3) document new options v6->v5: 1) split the patch into two: - ipvsadm: convert options to unsigned long long - ipvsadm: allow tunneling with gue encapsulation 2) do not mix static and dynamic allocation in fwd_tun_info 3) use correct nla_get/put function for tun_flags 4) fixed || style 5) use correct return value for parse_tun_type v5->v4: 1) add checksum support for gue encapsulation v4->v3: 1) removed changes to setsockopt interface 2) use correct nla_get/put function for tun_port v3->v2: 1) added missing break statements to a few switch cases v2->v1: 1) pass tun_type and tun_port as new optional parameters instead of a few bits in existing conn_flags parameters ============== Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
-rw-r--r--ipvsadm.870
-rw-r--r--ipvsadm.c426
-rw-r--r--libipvs/ip_vs.h28
-rw-r--r--libipvs/libipvs.c15
4 files changed, 473 insertions, 66 deletions
diff --git a/ipvsadm.8 b/ipvsadm.8
index 1b25888..256718e 100644
--- a/ipvsadm.8
+++ b/ipvsadm.8
@@ -339,6 +339,36 @@ the request sent to the virtual service.
.sp
\fB-i, --ipip\fR Use ipip encapsulation (tunneling).
.sp
+.ti +8
+.B --tun-type \fItun-type\fP
+.ti +16
+\fItun-type\fP is one of \fIipip\fP|\fIgue\fP.
+The default value of \fItun-type\fP is \fIipip\fP.
+.sp
+.ti +8
+.B --tun-port \fItun-port\fP
+.ti +16
+\fItun-port\fP is an integer specifying the destination port.
+Only valid for \fItun-type\fP \fIgue\fP.
+.sp
+.ti +8
+.B --tun-nocsum
+.ti +16
+Specify that UDP checksums are disabled. This is the default.
+Only valid for \fItun-type\fP \fIgue\fP.
+.sp
+.ti +8
+.B --tun-csum
+.ti +16
+Specify that UDP checksums are enabled.
+Only valid for \fItun-type\fP \fIgue\fP.
+.sp
+.ti +8
+.B --tun-remcsum
+.ti +16
+Specify that Remote Checksum Offload is enabled.
+Only valid for \fItun-type\fP \fIgue\fP.
+.sp
\fB-m, --masquerading\fR Use masquerading (network access translation, or NAT).
.sp
\fBNote:\fR Regardless of the packet-forwarding mechanism specified,
@@ -416,6 +446,11 @@ The \fIlist\fP command with the -c, --connection option and this option
will include persistence engine data, if any is present, when listing
connections.
.TP
+.B --tun-info
+Output of tunneling information. The \fIlist\fP command with this
+option will display the tunneling information of services and their
+servers.
+.TP
.B --sort
Sort the list of virtual services and real servers. The virtual
service entries are sorted in ascending order by <protocol, address,
@@ -553,6 +588,41 @@ modprobe ip_tables
iptables -A PREROUTING -t mangle -d 207.175.44.110/31 -j MARK --set-mark 1
modprobe ip_vs_ftp
.fi
+.SH EXAMPLE 3 - Virtual Service with GUE Tunneling
+The following commands configure a Linux Director to distribute
+incoming requests addressed to port 80 on 207.175.44.110 equally to
+port 80 on five real servers. The forwarding method used in this
+example is tunneling with gue encapsulation.
+.PP
+.nf
+ipvsadm -A -t 207.175.44.110:80 -s rr
+ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.1:80 -i --tun-type gue \
+--tun-port 6080 --tun-nocsum
+ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.2:80 -i --tun-type gue \
+--tun-port 6080 --tun-csum
+ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.3:80 -i --tun-type gue \
+--tun-port 6080 --tun-remcsum
+ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.4:80 -i --tun-type gue \
+--tun-port 6078
+ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.5:80 -i --tun-type gue \
+--tun-port 6079
+.fi
+.PP
+Alternatively, this could be achieved in a single ipvsadm command.
+.PP
+.nf
+echo "
+-A -t 207.175.44.110:80 -s rr
+-a -t 207.175.44.110:80 -r 192.168.10.1:80 -i --tun-type gue --tun-port 6080 \
+--tun-nocsum
+-a -t 207.175.44.110:80 -r 192.168.10.2:80 -i --tun-type gue --tun-port 6080 \
+--tun-csum
+-a -t 207.175.44.110:80 -r 192.168.10.3:80 -i --tun-type gue --tun-port 6080 \
+--tun-remcsum
+-a -t 207.175.44.110:80 -r 192.168.10.4:80 -i --tun-type gue --tun-port 6078
+-a -t 207.175.44.110:80 -r 192.168.10.5:80 -i --tun-type gue --tun-port 6079
+" | ipvsadm -R
+.fi
.SH IPv6
IPv6 addresses should be surrounded by square brackets ([ and ]).
.PP
diff --git a/ipvsadm.c b/ipvsadm.c
index e11aee0..6de9f0a 100644
--- a/ipvsadm.c
+++ b/ipvsadm.c
@@ -187,7 +187,48 @@ static const char* cmdnames[] = {
#define OPT_MCAST_PORT 0x02000000
#define OPT_MCAST_TTL 0x04000000
#define OPT_SYNC_MAXLEN 0x08000000
-#define NUMBER_OF_OPT 28
+#define OPT_TUN_INFO 0x10000000
+#define OPT_TUN_TYPE 0x20000000
+#define OPT_TUN_PORT 0x40000000
+#define OPT_TUN_NOCSUM 0x80000000
+#define OPT_TUN_CSUM 0x100000000
+#define OPT_TUN_REMCSUM 0x200000000
+#define NUMBER_OF_OPT 34
+
+#define OPTC_NUMERIC 0
+#define OPTC_CONNECTION 1
+#define OPTC_SERVICE 2
+#define OPTC_SCHEDULER 3
+#define OPTC_PERSISTENT 4
+#define OPTC_NETMASK 5
+#define OPTC_SERVER 6
+#define OPTC_FORWARD 7
+#define OPTC_WEIGHT 8
+#define OPTC_UTHRESHOLD 9
+#define OPTC_LTHRESHOLD 10
+#define OPTC_MCAST 11
+#define OPTC_TIMEOUT 12
+#define OPTC_DAEMON 13
+#define OPTC_STATS 14
+#define OPTC_RATE 15
+#define OPTC_THRESHOLDS 16
+#define OPTC_PERSISTENTCONN 17
+#define OPTC_NOSORT 18
+#define OPTC_SYNCID 19
+#define OPTC_EXACT 20
+#define OPTC_ONEPACKET 21
+#define OPTC_PERSISTENCE_ENGINE 22
+#define OPTC_SCHED_FLAGS 23
+#define OPTC_MCAST_GROUP 24
+#define OPTC_MCAST_PORT 25
+#define OPTC_MCAST_TTL 26
+#define OPTC_SYNC_MAXLEN 27
+#define OPTC_TUN_INFO 28
+#define OPTC_TUN_TYPE 29
+#define OPTC_TUN_PORT 30
+#define OPTC_TUN_NOCSUM 31
+#define OPTC_TUN_CSUM 32
+#define OPTC_TUN_REMCSUM 33
static const char* optnames[] = {
"numeric",
@@ -218,6 +259,12 @@ static const char* optnames[] = {
"mcast-port",
"mcast-ttl",
"sync-maxlen",
+ "tun-info",
+ "tun-type",
+ "tun-port",
+ "tun-nocsum",
+ "tun-csum",
+ "tun-remcsum",
};
/*
@@ -230,21 +277,63 @@ static const char* optnames[] = {
*/
static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
{
- /* -n -c svc -s -p -M -r fwd -w -x -y -mc tot dmn -st -rt thr -pc srt sid -ex ops -pe -b grp port ttl size */
-/*ADD*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x'},
-/*EDIT*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x'},
-/*DEL*/ {'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*FLUSH*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*LIST*/ {' ', '1', '1', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', '1', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*ADDSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*DELSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*EDITSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*TIMEOUT*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*STARTD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' '},
-/*STOPD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*RESTORE*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*SAVE*/ {' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
-/*ZERO*/ {'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+ /* -n -c svc -s -p -M -r fwd -w -x -y -mc tot dmn -st -rt thr -pc srt sid -ex ops -pe -b grp port ttl size tinf type tprt nocs csum remc */
+/*ADD*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*EDIT*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*DEL*/ {'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*FLUSH*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*LIST*/ {' ', '1', '1', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', '1', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x'},
+/*ADDSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', ' '},
+/*DELSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*EDITSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', ' '},
+/*TIMEOUT*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*STARTD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*STOPD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*RESTORE*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*SAVE*/ {' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+/*ZERO*/ {'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'},
+};
+
+static const char * const tunnames[] = {
+ "ipip",
+ "gue",
+};
+
+static const char * const tunflags[] = {
+ "-c", /* without checksum */
+ "+c", /* with checksum */
+ "r+c", /* with remote checksum */
+};
+
+static const char * const tun_flags_opts[] = {
+ "--tun-nocsum",
+ "--tun-csum",
+ "--tun-remcsum",
+};
+
+static const int tunopts[] = {
+ OPTC_TUN_PORT,
+ OPTC_TUN_NOCSUM,
+ OPTC_TUN_CSUM,
+ OPTC_TUN_REMCSUM,
+};
+
+#define NUMBER_OF_TUN_OPT 4
+#define NA "n/a"
+
+/*
+ * Table of legal combinations of tunnel types and options.
+ * Key:
+ * '+' compulsory
+ * 'x' illegal
+ * '1' exclusive (only one '1' option can be supplied)
+ * ' ' optional
+ */
+static const char
+tunnel_types_v_options[IP_VS_CONN_F_TUNNEL_TYPE_MAX][NUMBER_OF_TUN_OPT] = {
+ /* tprt nocs csum remc */
+/* ipip */ {'x', 'x', 'x', 'x'},
+/* gue */ {'+', '1', '1', '1'},
};
/* printing format flags */
@@ -257,6 +346,7 @@ static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
#define FMT_PERSISTENTCONN 0x0020
#define FMT_NOSORT 0x0040
#define FMT_EXACT 0x0080
+#define FMT_TUN_INFO 0x0100
#define SERVICE_NONE 0x0000
#define SERVICE_ADDR 0x0001
@@ -265,6 +355,9 @@ static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* default scheduler */
#define DEF_SCHED "wlc"
+/* default tunnel type */
+#define DEF_TUNNEL_TYPE "ipip"
+
/* default multicast interface name */
#define DEF_MCAST_IFN "eth0"
@@ -300,6 +393,12 @@ enum {
TAG_MCAST_PORT,
TAG_MCAST_TTL,
TAG_SYNC_MAXLEN,
+ TAG_TUN_INFO,
+ TAG_TUN_TYPE,
+ TAG_TUN_PORT,
+ TAG_TUN_NOCSUM,
+ TAG_TUN_CSUM,
+ TAG_TUN_REMCSUM,
};
/* various parsing helpers & parsing functions */
@@ -318,11 +417,15 @@ static int parse_netmask(char *buf, u_int32_t *addr);
static int parse_timeout(char *buf, int min, int max);
static unsigned int parse_fwmark(char *buf);
static unsigned int parse_sched_flags(const char *sched, char *optarg);
+static int parse_tun_type(const char *name);
/* check the options based on the commands_v_options table */
-static void generic_opt_check(int command, int options);
+static void generic_opt_check(int command, unsigned long long options);
static void set_command(int *cmd, const int newcmd);
-static void set_option(unsigned int *options, unsigned int option);
+static void set_option(unsigned long long *options, int optc);
+
+/* check the options based on the tunnel_types_v_options table */
+static void tunnel_opt_check(int tun_type, unsigned long long options);
static void tryhelp_exit(const char *program, const int exit_status);
static void usage_exit(const char *program, const int exit_status);
@@ -416,7 +519,7 @@ static char *protocol_name(int proto)
static int
parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
- unsigned int *options, unsigned int *format)
+ unsigned long long *options, unsigned int *format)
{
int c, parse;
poptContext context;
@@ -495,6 +598,18 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
TAG_MCAST_TTL, NULL, NULL },
{ "sync-maxlen", '\0', POPT_ARG_STRING, &optarg,
TAG_SYNC_MAXLEN, NULL, NULL },
+ { "tun-info", '\0', POPT_ARG_NONE, NULL, TAG_TUN_INFO,
+ NULL, NULL },
+ { "tun-type", '\0', POPT_ARG_STRING, &optarg, TAG_TUN_TYPE,
+ NULL, NULL },
+ { "tun-port", '\0', POPT_ARG_STRING, &optarg, TAG_TUN_PORT,
+ NULL, NULL },
+ { "tun-nocsum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_NOCSUM,
+ NULL, NULL },
+ { "tun-csum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_CSUM,
+ NULL, NULL },
+ { "tun-remcsum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_REMCSUM,
+ NULL, NULL },
{ NULL, 0, 0, NULL, 0, NULL, NULL }
};
@@ -575,7 +690,7 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
case 't':
case 'u':
case TAG_SCTP_SERVICE:
- set_option(options, OPT_SERVICE);
+ set_option(options, OPTC_SERVICE);
ce->svc.protocol = option_to_protocol(c);
parse = parse_service(optarg, &ce->svc);
if (!(parse & SERVICE_ADDR))
@@ -583,7 +698,7 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
"address[:port] specified");
break;
case 'f':
- set_option(options, OPT_SERVICE);
+ set_option(options, OPTC_SERVICE);
/*
* Set protocol to a sane values, even
* though it is not used
@@ -593,18 +708,18 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
ce->svc.fwmark = parse_fwmark(optarg);
break;
case 's':
- set_option(options, OPT_SCHEDULER);
+ set_option(options, OPTC_SCHEDULER);
strncpy(ce->svc.sched_name,
optarg, IP_VS_SCHEDNAME_MAXLEN - 1);
break;
case 'p':
- set_option(options, OPT_PERSISTENT);
+ set_option(options, OPTC_PERSISTENT);
ce->svc.flags |= IP_VS_SVC_F_PERSISTENT;
ce->svc.timeout =
parse_timeout(optarg, 1, MAX_TIMEOUT);
break;
case 'M':
- set_option(options, OPT_NETMASK);
+ set_option(options, OPTC_NETMASK);
if (ce->svc.af != AF_INET6) {
parse = parse_netmask(optarg, &ce->svc.netmask);
if (parse != 1)
@@ -617,7 +732,7 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
}
break;
case 'r':
- set_option(options, OPT_SERVER);
+ set_option(options, OPTC_SERVER);
ipvs_service_t t_dest = ce->svc;
parse = parse_service(optarg, &t_dest);
ce->dest.af = t_dest.af;
@@ -631,84 +746,84 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
ce->dest.port = ce->svc.port;
break;
case 'i':
- set_option(options, OPT_FORWARD);
+ set_option(options, OPTC_FORWARD);
ce->dest.conn_flags = IP_VS_CONN_F_TUNNEL;
break;
case 'g':
- set_option(options, OPT_FORWARD);
+ set_option(options, OPTC_FORWARD);
ce->dest.conn_flags = IP_VS_CONN_F_DROUTE;
break;
case 'm':
- set_option(options, OPT_FORWARD);
+ set_option(options, OPTC_FORWARD);
ce->dest.conn_flags = IP_VS_CONN_F_MASQ;
break;
case 'w':
- set_option(options, OPT_WEIGHT);
+ set_option(options, OPTC_WEIGHT);
if ((ce->dest.weight =
string_to_number(optarg, 0, 65535)) == -1)
fail(2, "illegal weight specified");
break;
case 'x':
- set_option(options, OPT_UTHRESHOLD);
+ set_option(options, OPTC_UTHRESHOLD);
if ((ce->dest.u_threshold =
string_to_number(optarg, 0, INT_MAX)) == -1)
fail(2, "illegal u_threshold specified");
break;
case 'y':
- set_option(options, OPT_LTHRESHOLD);
+ set_option(options, OPTC_LTHRESHOLD);
if ((ce->dest.l_threshold =
string_to_number(optarg, 0, INT_MAX)) == -1)
fail(2, "illegal l_threshold specified");
break;
case 'c':
- set_option(options, OPT_CONNECTION);
+ set_option(options, OPTC_CONNECTION);
break;
case 'n':
- set_option(options, OPT_NUMERIC);
+ set_option(options, OPTC_NUMERIC);
*format |= FMT_NUMERIC;
break;
case TAG_MCAST_INTERFACE:
- set_option(options, OPT_MCAST);
+ set_option(options, OPTC_MCAST);
strncpy(ce->daemon.mcast_ifn,
optarg, IP_VS_IFNAME_MAXLEN - 1);
break;
case 'I':
- set_option(options, OPT_SYNCID);
+ set_option(options, OPTC_SYNCID);
if ((ce->daemon.syncid =
string_to_number(optarg, 0, 255)) == -1)
fail(2, "illegal syncid specified");
break;
case TAG_TIMEOUT:
- set_option(options, OPT_TIMEOUT);
+ set_option(options, OPTC_TIMEOUT);
break;
case TAG_DAEMON:
- set_option(options, OPT_DAEMON);
+ set_option(options, OPTC_DAEMON);
break;
case TAG_STATS:
- set_option(options, OPT_STATS);
+ set_option(options, OPTC_STATS);
*format |= FMT_STATS;
break;
case TAG_RATE:
- set_option(options, OPT_RATE);
+ set_option(options, OPTC_RATE);
*format |= FMT_RATE;
break;
case TAG_THRESHOLDS:
- set_option(options, OPT_THRESHOLDS);
+ set_option(options, OPTC_THRESHOLDS);
*format |= FMT_THRESHOLDS;
break;
case TAG_PERSISTENTCONN:
- set_option(options, OPT_PERSISTENTCONN);
+ set_option(options, OPTC_PERSISTENTCONN);
*format |= FMT_PERSISTENTCONN;
break;
case TAG_NO_SORT:
- set_option(options, OPT_NOSORT );
+ set_option(options, OPTC_NOSORT);
*format |= FMT_NOSORT;
break;
case TAG_SORT:
/* Sort is the default, this is a no-op for compatibility */
break;
case 'X':
- set_option(options, OPT_EXACT);
+ set_option(options, OPTC_EXACT);
*format |= FMT_EXACT;
break;
case '6':
@@ -720,20 +835,20 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
}
break;
case 'o':
- set_option(options, OPT_ONEPACKET);
+ set_option(options, OPTC_ONEPACKET);
ce->svc.flags |= IP_VS_SVC_F_ONEPACKET;
break;
case TAG_PERSISTENCE_ENGINE:
- set_option(options, OPT_PERSISTENCE_ENGINE);
+ set_option(options, OPTC_PERSISTENCE_ENGINE);
strncpy(ce->svc.pe_name, optarg, IP_VS_PENAME_MAXLEN);
break;
case 'b':
- set_option(options, OPT_SCHED_FLAGS);
+ set_option(options, OPTC_SCHED_FLAGS);
snprintf(sched_flags_arg, sizeof(sched_flags_arg),
"%s", optarg);
break;
case TAG_MCAST_GROUP:
- set_option(options, OPT_MCAST_GROUP);
+ set_option(options, OPTC_MCAST_GROUP);
if (strchr(optarg, ':')) {
if (inet_pton(AF_INET6, optarg,
&ce->daemon.mcast_group) <= 0 ||
@@ -753,26 +868,56 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce,
}
break;
case TAG_MCAST_PORT:
- set_option(options, OPT_MCAST_PORT);
+ set_option(options, OPTC_MCAST_PORT);
parse = string_to_number(optarg, 1, 65535);
if (parse == -1)
fail(2, "illegal mcast-port specified");
ce->daemon.mcast_port = parse;
break;
case TAG_MCAST_TTL:
- set_option(options, OPT_MCAST_TTL);
+ set_option(options, OPTC_MCAST_TTL);
parse = string_to_number(optarg, 1, 255);
if (parse == -1)
fail(2, "illegal mcast-ttl specified");
ce->daemon.mcast_ttl = parse;
break;
case TAG_SYNC_MAXLEN:
- set_option(options, OPT_SYNC_MAXLEN);
+ set_option(options, OPTC_SYNC_MAXLEN);
parse = string_to_number(optarg, 1, 65535 - 20 - 8);
if (parse == -1)
fail(2, "illegal sync-maxlen specified");
ce->daemon.sync_maxlen = parse;
break;
+ case TAG_TUN_INFO:
+ set_option(options, OPTC_TUN_INFO);
+ *format |= FMT_TUN_INFO;
+ break;
+ case TAG_TUN_TYPE:
+ set_option(options, OPTC_TUN_TYPE);
+ parse = parse_tun_type(optarg);
+ if (parse == -1)
+ fail(2, "illegal tunnel type specified");
+ ce->dest.tun_type = parse;
+ break;
+ case TAG_TUN_PORT:
+ set_option(options, OPTC_TUN_PORT);
+ parse = string_to_number(optarg, 1, 65535);
+ if (parse == -1)
+ fail(2, "illegal tunnel port specified");
+ ce->dest.tun_port = htons(parse);
+ break;
+ case TAG_TUN_NOCSUM:
+ set_option(options, OPTC_TUN_NOCSUM);
+ ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM;
+ break;
+ case TAG_TUN_CSUM:
+ set_option(options, OPTC_TUN_CSUM);
+ ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_CSUM;
+ break;
+ case TAG_TUN_REMCSUM:
+ set_option(options, OPTC_TUN_REMCSUM);
+ ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM;
+ break;
default:
fail(2, "invalid option `%s'",
poptBadOption(context, POPT_BADOPTION_NOALIAS));
@@ -845,14 +990,21 @@ static int restore_table(int argc, char **argv, int reading_stdin)
static int process_options(int argc, char **argv, int reading_stdin)
{
struct ipvs_command_entry ce;
- unsigned int options = OPT_NONE;
+ unsigned long long options = OPT_NONE;
unsigned int format = FMT_NONE;
+ unsigned int fwd_method;
int result = 0;
memset(&ce, 0, sizeof(struct ipvs_command_entry));
ce.cmd = CMD_NONE;
/* Set the default weight 1 */
ce.dest.weight = 1;
+ /* Set the default tunnel type 0(ipip) */
+ ce.dest.tun_type = 0;
+ /* Set the default tunnel port 0(n/a) */
+ ce.dest.tun_port = 0;
+ /* Set the default tunnel flags 0(nocsum) */
+ ce.dest.tun_flags = 0;
/* Set direct routing as default forwarding method */
ce.dest.conn_flags = IP_VS_CONN_F_DROUTE;
/* Set the default persistent granularity to /32 mask */
@@ -883,6 +1035,8 @@ static int process_options(int argc, char **argv, int reading_stdin)
if (ce.cmd == CMD_STARTDAEMON && strlen(ce.daemon.mcast_ifn) == 0)
strcpy(ce.daemon.mcast_ifn, DEF_MCAST_IFN);
+ fwd_method = ce.dest.conn_flags & IP_VS_CONN_F_FWD_MASK;
+
if (ce.cmd == CMD_ADDDEST || ce.cmd == CMD_EDITDEST) {
/*
* The destination port must be equal to the service port
@@ -890,15 +1044,25 @@ static int process_options(int argc, char **argv, int reading_stdin)
* Don't worry about this if fwmark is used.
*/
if (!ce.svc.fwmark &&
- (ce.dest.conn_flags == IP_VS_CONN_F_TUNNEL
- || ce.dest.conn_flags == IP_VS_CONN_F_DROUTE))
+ (fwd_method == IP_VS_CONN_F_TUNNEL ||
+ fwd_method == IP_VS_CONN_F_DROUTE))
ce.dest.port = ce.svc.port;
/* Tunneling allows different address family */
if (ce.dest.af != ce.svc.af &&
- ce.dest.conn_flags != IP_VS_CONN_F_TUNNEL)
+ fwd_method != IP_VS_CONN_F_TUNNEL)
fail(2, "Different address family is allowed only "
"for tunneling servers");
+
+ /* Only tunneling allows tunnel options */
+ if (((options & (OPT_TUN_TYPE | OPT_TUN_PORT)) ||
+ (options & (OPT_TUN_NOCSUM | OPT_TUN_CSUM)) ||
+ (options & OPT_TUN_REMCSUM)) &&
+ fwd_method != IP_VS_CONN_F_TUNNEL)
+ fail(2,
+ "Tunnel options conflict with forward method");
+
+ tunnel_opt_check(ce.dest.tun_type, options);
}
switch (ce.cmd) {
@@ -1163,8 +1327,22 @@ static unsigned int parse_sched_flags(const char *sched, char *optarg)
return flags;
}
+static int parse_tun_type(const char *tun_type)
+{
+ int type = -1;
+
+ if (!strcmp(tun_type, "ipip"))
+ type = IP_VS_CONN_F_TUNNEL_TYPE_IPIP;
+ else if (!strcmp(tun_type, "gue"))
+ type = IP_VS_CONN_F_TUNNEL_TYPE_GUE;
+ else
+ type = -1;
+
+ return type;
+}
+
static void
-generic_opt_check(int command, int options)
+generic_opt_check(int command, unsigned long long options)
{
int i, j;
int last = 0, count = 0;
@@ -1173,7 +1351,7 @@ generic_opt_check(int command, int options)
i = command - CMD_NONE -1;
for (j = 0; j < NUMBER_OF_OPT; j++) {
- if (!(options & (1<<j))) {
+ if (!(options & (1ULL << j))) {
if (commands_v_options[i][j] == '+')
fail(2, "You need to supply the '%s' "
"option for the '%s' command",
@@ -1197,13 +1375,39 @@ generic_opt_check(int command, int options)
}
}
-static inline const char *
-opt2name(int option)
+static void
+tunnel_opt_check(int tun_type, unsigned long long options)
{
- const char **ptr;
- for (ptr = optnames; option > 1; option >>= 1, ptr++);
+ int i, j, k;
+ int last = 0, count = 0;
+
+ /* Check that tunnel types are valid with options. */
+ i = tun_type;
- return *ptr;
+ for (j = 0; j < NUMBER_OF_TUN_OPT; j++) {
+ k = tunopts[j];
+ if (!(options & (1ULL << k))) {
+ if (tunnel_types_v_options[i][j] == '+')
+ fail(2, "You need to supply the '%s' "
+ "option for the '%s' tunnel type",
+ optnames[k], tunnames[i]);
+ } else {
+ if (tunnel_types_v_options[i][j] == 'x')
+ fail(2, "Illegal '%s' option with "
+ "the '%s' tunnel type",
+ optnames[k], tunnames[i]);
+ if (tunnel_types_v_options[i][j] == '1') {
+ count++;
+ if (count == 1) {
+ last = k;
+ continue;
+ }
+ fail(2, "The option '%s' conflicts with the "
+ "'%s' option in the '%s' tunnel type",
+ optnames[k], optnames[last], tunnames[i]);
+ }
+ }
+ }
}
static void
@@ -1215,10 +1419,11 @@ set_command(int *cmd, const int newcmd)
}
static void
-set_option(unsigned int *options, unsigned int option)
+set_option(unsigned long long *options, int optc)
{
+ unsigned long long option = 1ULL << optc;
if (*options & option)
- fail(2, "multiple '%s' options specified", opt2name(option));
+ fail(2, "multiple '%s' options specified", optnames[optc]);
*options |= option;
}
@@ -1301,6 +1506,12 @@ static void usage_exit(const char *program, const int exit_status)
" --gatewaying -g gatewaying (direct routing) (default)\n"
" --ipip -i ipip encapsulation (tunneling)\n"
" --masquerading -m masquerading (NAT)\n"
+ " --tun-type type one of ipip|gue,\n"
+ " the default tunnel type is %s.\n"
+ " --tun-port port tunnel destination port\n"
+ " --tun-nocsum tunnel encapsulation without checksum\n"
+ " --tun-csum tunnel encapsulation with checksum\n"
+ " --tun-remcsum tunnel encapsulation with remote checksum\n"
" --weight -w weight capacity of real server\n"
" --u-threshold -x uthreshold upper threshold of connections\n"
" --l-threshold -y lthreshold lower threshold of connections\n"
@@ -1312,12 +1523,13 @@ static void usage_exit(const char *program, const int exit_status)
" --exact expand numbers (display exact values)\n"
" --thresholds output of thresholds information\n"
" --persistent-conn output of persistent connection info\n"
+ " --tun-info output of tunnel information\n"
" --nosort disable sorting output of service/server entries\n"
" --sort does nothing, for backwards compatibility\n"
" --ops -o one-packet scheduling\n"
" --numeric -n numeric output of addresses and ports\n"
" --sched-flags -b flags scheduler flags (comma-separated)\n",
- DEF_SCHED);
+ DEF_SCHED, DEF_TUNNEL_TYPE);
fprintf(stream,
"Daemon Options:\n"
@@ -1565,6 +1777,36 @@ static inline char *fwd_switch(unsigned flags)
}
+static inline char *fwd_tun_info(ipvs_dest_entry_t *e)
+{
+ char *info = malloc(16);
+
+ if (!info)
+ return NULL;
+
+ switch (e->conn_flags & IP_VS_CONN_F_FWD_MASK) {
+ case IP_VS_CONN_F_TUNNEL:
+ switch (e->tun_type) {
+ case IP_VS_CONN_F_TUNNEL_TYPE_IPIP:
+ snprintf(info, 16, "%s", tunnames[e->tun_type]);
+ break;
+ case IP_VS_CONN_F_TUNNEL_TYPE_GUE:
+ snprintf(info, 16, "%s:%d:%s",
+ tunnames[e->tun_type], ntohs(e->tun_port),
+ tunflags[e->tun_flags]);
+ break;
+ default:
+ free(info);
+ return NULL;
+ }
+ break;
+ default:
+ free(info);
+ return NULL;
+ }
+ return info;
+}
+
static void print_largenum(unsigned long long i, unsigned int format)
{
if (format & FMT_EXACT) {
@@ -1641,12 +1883,47 @@ static void print_title(unsigned int format)
" -> RemoteAddress:Port\n",
"Prot LocalAddress:Port",
"Weight", "PersistConn", "ActiveConn", "InActConn");
+ else if ((format & FMT_TUN_INFO))
+ printf("Prot LocalAddress:Port Scheduler Flags\n"
+ " -> RemoteAddress:Port Forward TunnelInfo Weight ActiveConn InActConn\n");
else if (!(format & FMT_RULE))
printf("Prot LocalAddress:Port Scheduler Flags\n"
" -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n");
}
+static inline void
+print_tunnel_rule(char *svc_name, char *dname, ipvs_dest_entry_t *e)
+{
+ switch (e->tun_type) {
+ case IP_VS_CONN_F_TUNNEL_TYPE_GUE:
+ printf("-a %s -r %s %s -w %d --tun-type %s --tun-port %d %s\n",
+ svc_name,
+ dname,
+ fwd_switch(e->conn_flags),
+ e->weight,
+ tunnames[e->tun_type],
+ ntohs(e->tun_port),
+ tun_flags_opts[e->tun_flags]);
+ break;
+ case IP_VS_CONN_F_TUNNEL_TYPE_IPIP:
+ printf("-a %s -r %s %s -w %d --tun-type %s\n",
+ svc_name,
+ dname,
+ fwd_switch(e->conn_flags),
+ e->weight,
+ tunnames[e->tun_type]);
+ break;
+ default:
+ printf("-a %s -r %s %s -w %d\n",
+ svc_name,
+ dname,
+ fwd_switch(e->conn_flags),
+ e->weight);
+ break;
+ }
+}
+
static void
print_service_entry(ipvs_service_entry_t *se, unsigned int format)
{
@@ -1768,6 +2045,7 @@ print_service_entry(ipvs_service_entry_t *se, unsigned int format)
for (i = 0; i < d->num_dests; i++) {
char *dname;
ipvs_dest_entry_t *e = &d->entrytable[i];
+ unsigned int fwd_method = e->conn_flags & IP_VS_CONN_F_FWD_MASK;
if (!(dname = addrport_to_anyname(e->af, &(e->addr), ntohs(e->port),
se->protocol, format))) {
@@ -1778,8 +2056,15 @@ print_service_entry(ipvs_service_entry_t *se, unsigned int format)
dname[28] = '\0';
if (format & FMT_RULE) {
- printf("-a %s -r %s %s -w %d\n", svc_name, dname,
- fwd_switch(e->conn_flags), e->weight);
+ if (fwd_method == IP_VS_CONN_F_TUNNEL) {
+ print_tunnel_rule(svc_name, dname, e);
+ } else {
+ printf("-a %s -r %s %s -w %d\n",
+ svc_name,
+ dname,
+ fwd_switch(e->conn_flags),
+ e->weight);
+ }
} else if (format & FMT_STATS) {
printf(" -> %-28s", dname);
print_largenum(e->stats64.conns, format);
@@ -1804,6 +2089,15 @@ print_service_entry(ipvs_service_entry_t *se, unsigned int format)
printf(" -> %-28s %-9u %-11u %-10u %-10u\n", dname,
e->weight, e->persistconns,
e->activeconns, e->inactconns);
+ } else if (format & FMT_TUN_INFO) {
+ char *ti = fwd_tun_info(e);
+
+ printf(" -> %-28s %-7s %-13s %-6d %-10u %-10u\n",
+ dname, fwd_name(e->conn_flags),
+ ti ? : NA,
+ e->weight, e->activeconns, e->inactconns);
+
+ free(ti);
} else
printf(" -> %-28s %-7s %-6d %-10u %-10u\n",
dname, fwd_name(e->conn_flags),
diff --git a/libipvs/ip_vs.h b/libipvs/ip_vs.h
index ad0141c..fa3770c 100644
--- a/libipvs/ip_vs.h
+++ b/libipvs/ip_vs.h
@@ -107,6 +107,18 @@
#define IP_VS_PEDATA_MAXLEN 255
+/* Tunnel types */
+enum {
+ IP_VS_CONN_F_TUNNEL_TYPE_IPIP = 0, /* IPIP */
+ IP_VS_CONN_F_TUNNEL_TYPE_GUE, /* GUE */
+ IP_VS_CONN_F_TUNNEL_TYPE_MAX,
+};
+
+/* Tunnel encapsulation flags */
+#define IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM (0)
+#define IP_VS_TUNNEL_ENCAP_FLAG_CSUM (1 << 0)
+#define IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM (1 << 1)
+
union nf_inet_addr {
__u32 all[4];
__be32 ip;
@@ -178,6 +190,11 @@ struct ip_vs_dest_user {
u_int32_t l_threshold; /* lower threshold */
u_int16_t af;
union nf_inet_addr addr;
+
+ /* tunnel info */
+ u_int16_t tun_type; /* tunnel type */
+ __be16 tun_port; /* tunnel port */
+ u_int16_t tun_flags; /* tunnel flags */
};
/*
@@ -313,6 +330,11 @@ struct ip_vs_dest_entry {
/* statistics, 64-bit */
struct ip_vs_stats64 stats64;
+
+ /* tunnel info */
+ u_int16_t tun_type; /* tunnel type */
+ __be16 tun_port; /* tunnel port */
+ u_int16_t tun_flags; /* tunnel flags */
};
/* The argument to IP_VS_SO_GET_DESTS */
@@ -527,6 +549,12 @@ enum {
IPVS_DEST_ATTR_STATS64, /* nested attribute for dest stats */
+ IPVS_DEST_ATTR_TUN_TYPE, /* tunnel type */
+
+ IPVS_DEST_ATTR_TUN_PORT, /* tunnel port */
+
+ IPVS_DEST_ATTR_TUN_FLAGS, /* tunnel flags */
+
__IPVS_DEST_ATTR_MAX,
};
diff --git a/libipvs/libipvs.c b/libipvs/libipvs.c
index 9be7700..067306a 100644
--- a/libipvs/libipvs.c
+++ b/libipvs/libipvs.c
@@ -390,6 +390,9 @@ static int ipvs_nl_fill_dest_attr(struct nl_msg *msg, ipvs_dest_t *dst)
NLA_PUT_U16(msg, IPVS_DEST_ATTR_PORT, dst->port);
NLA_PUT_U32(msg, IPVS_DEST_ATTR_FWD_METHOD, dst->conn_flags & IP_VS_CONN_F_FWD_MASK);
NLA_PUT_U32(msg, IPVS_DEST_ATTR_WEIGHT, dst->weight);
+ NLA_PUT_U8(msg, IPVS_DEST_ATTR_TUN_TYPE, dst->tun_type);
+ NLA_PUT_U16(msg, IPVS_DEST_ATTR_TUN_PORT, dst->tun_port);
+ NLA_PUT_U16(msg, IPVS_DEST_ATTR_TUN_FLAGS, dst->tun_flags);
NLA_PUT_U32(msg, IPVS_DEST_ATTR_U_THRESH, dst->u_threshold);
NLA_PUT_U32(msg, IPVS_DEST_ATTR_L_THRESH, dst->l_threshold);
@@ -856,6 +859,9 @@ static int ipvs_dests_parse_cb(struct nl_msg *msg, void *arg)
struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
struct nlattr *dest_attrs[IPVS_DEST_ATTR_MAX + 1];
struct nlattr *attr_addr_family = NULL;
+ struct nlattr *attr_tun_type = NULL;
+ struct nlattr *attr_tun_port = NULL;
+ struct nlattr *attr_tun_flags = NULL;
struct ip_vs_get_dests **dp = (struct ip_vs_get_dests **)arg;
struct ip_vs_get_dests *d = (struct ip_vs_get_dests *)*dp;
int i = d->num_dests;
@@ -888,6 +894,15 @@ static int ipvs_dests_parse_cb(struct nl_msg *msg, void *arg)
d->entrytable[i].port = nla_get_u16(dest_attrs[IPVS_DEST_ATTR_PORT]);
d->entrytable[i].conn_flags = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_FWD_METHOD]);
d->entrytable[i].weight = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_WEIGHT]);
+ attr_tun_type = dest_attrs[IPVS_DEST_ATTR_TUN_TYPE];
+ if (attr_tun_type)
+ d->entrytable[i].tun_type = nla_get_u8(attr_tun_type);
+ attr_tun_port = dest_attrs[IPVS_DEST_ATTR_TUN_PORT];
+ if (attr_tun_port)
+ d->entrytable[i].tun_port = nla_get_u16(attr_tun_port);
+ attr_tun_flags = dest_attrs[IPVS_DEST_ATTR_TUN_FLAGS];
+ if (attr_tun_flags)
+ d->entrytable[i].tun_flags = nla_get_u16(attr_tun_flags);
d->entrytable[i].u_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_U_THRESH]);
d->entrytable[i].l_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_L_THRESH]);
d->entrytable[i].activeconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS]);