Commit 49888961 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mptcp-add-mp_prio-support-and-rework-local-address-ids'

Mat Martineau says:

====================
MPTCP: Add MP_PRIO support and rework local address IDs

Patches 1 and 2 rework the assignment of local address IDs to allow them
to be assigned by a userspace path manager, and add corresponding self
tests.

Patches 2-8 add the ability to change subflow priority after a subflow
has been established. Each subflow in a MPTCP connection has a priority
level: "regular" or "backup". Data should only be sent on backup
subflows if no regular subflows are available. The priority level can be
set when the subflow connection is established (as was already
implemented), or during the life of the connection by sending MP_PRIO in
the TCP options (as added here). Self tests are included.
====================

Link: https://lore.kernel.org/r/20210109004802.341602-1-mathew.j.martineau@linux.intel.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 43b39834 718eb44e
......@@ -82,6 +82,7 @@ enum {
MPTCP_PM_CMD_FLUSH_ADDRS,
MPTCP_PM_CMD_SET_LIMITS,
MPTCP_PM_CMD_GET_LIMITS,
MPTCP_PM_CMD_SET_FLAGS,
__MPTCP_PM_CMD_AFTER_LAST
};
......
......@@ -31,6 +31,8 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("EchoAdd", MPTCP_MIB_ECHOADD),
SNMP_MIB_ITEM("RmAddr", MPTCP_MIB_RMADDR),
SNMP_MIB_ITEM("RmSubflow", MPTCP_MIB_RMSUBFLOW),
SNMP_MIB_ITEM("MPPrioTx", MPTCP_MIB_MPPRIOTX),
SNMP_MIB_ITEM("MPPrioRx", MPTCP_MIB_MPPRIORX),
SNMP_MIB_SENTINEL
};
......
......@@ -24,6 +24,8 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_ECHOADD, /* Received ADD_ADDR with echo-flag=1 */
MPTCP_MIB_RMADDR, /* Received RM_ADDR */
MPTCP_MIB_RMSUBFLOW, /* Remove a subflow */
MPTCP_MIB_MPPRIOTX, /* Transmit a MP_PRIO */
MPTCP_MIB_MPPRIORX, /* Received a MP_PRIO */
__MPTCP_MIB_MAX
};
......
......@@ -282,6 +282,15 @@ static void mptcp_parse_option(const struct sk_buff *skb,
pr_debug("RM_ADDR: id=%d", mp_opt->rm_id);
break;
case MPTCPOPT_MP_PRIO:
if (opsize != TCPOLEN_MPTCP_PRIO)
break;
mp_opt->mp_prio = 1;
mp_opt->backup = *ptr++ & MPTCP_PRIO_BKUP;
pr_debug("MP_PRIO: prio=%d", mp_opt->backup);
break;
case MPTCPOPT_MP_FASTCLOSE:
if (opsize != TCPOLEN_MPTCP_FASTCLOSE)
break;
......@@ -313,6 +322,7 @@ void mptcp_get_options(const struct sk_buff *skb,
mp_opt->port = 0;
mp_opt->rm_addr = 0;
mp_opt->dss = 0;
mp_opt->mp_prio = 0;
length = (th->doff * 4) - sizeof(struct tcphdr);
ptr = (const unsigned char *)(th + 1);
......@@ -679,6 +689,28 @@ static bool mptcp_established_options_rm_addr(struct sock *sk,
return true;
}
static bool mptcp_established_options_mp_prio(struct sock *sk,
unsigned int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
if (!subflow->send_mp_prio)
return false;
if (remaining < TCPOLEN_MPTCP_PRIO)
return false;
*size = TCPOLEN_MPTCP_PRIO;
opts->suboptions |= OPTION_MPTCP_PRIO;
opts->backup = subflow->request_bkup;
pr_debug("prio=%d", opts->backup);
return true;
}
bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int *size, unsigned int remaining,
struct mptcp_out_options *opts)
......@@ -721,6 +753,12 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
ret = true;
}
if (mptcp_established_options_mp_prio(sk, &opt_size, remaining, opts)) {
*size += opt_size;
remaining -= opt_size;
ret = true;
}
return ret;
}
......@@ -994,6 +1032,12 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
mp_opt.rm_addr = 0;
}
if (mp_opt.mp_prio) {
mptcp_pm_mp_prio_received(sk, mp_opt.backup);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPPRIORX);
mp_opt.mp_prio = 0;
}
if (!mp_opt.dss)
return;
......@@ -1168,6 +1212,18 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp,
0, opts->rm_id);
}
if (OPTION_MPTCP_PRIO & opts->suboptions) {
const struct sock *ssk = (const struct sock *)tp;
struct mptcp_subflow_context *subflow;
subflow = mptcp_subflow_ctx(ssk);
subflow->send_mp_prio = 0;
*ptr++ = mptcp_option(MPTCPOPT_MP_PRIO,
TCPOLEN_MPTCP_PRIO,
opts->backup, TCPOPT_NOP);
}
if (OPTION_MPTCP_MPJ_SYN & opts->suboptions) {
*ptr++ = mptcp_option(MPTCPOPT_MP_JOIN,
TCPOLEN_MPTCP_MPJ_SYN,
......
......@@ -207,6 +207,14 @@ void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id)
spin_unlock_bh(&pm->lock);
}
void mptcp_pm_mp_prio_received(struct sock *sk, u8 bkup)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
pr_debug("subflow->backup=%d, bkup=%d\n", subflow->backup, bkup);
subflow->backup = bkup;
}
/* path manager helpers */
bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
......
......@@ -36,6 +36,9 @@ struct mptcp_pm_add_entry {
u8 retrans_times;
};
#define MAX_ADDR_ID 255
#define BITMAP_SZ DIV_ROUND_UP(MAX_ADDR_ID + 1, BITS_PER_LONG)
struct pm_nl_pernet {
/* protects pernet updates */
spinlock_t lock;
......@@ -46,6 +49,7 @@ struct pm_nl_pernet {
unsigned int local_addr_max;
unsigned int subflows_max;
unsigned int next_id;
unsigned long id_bitmap[BITMAP_SZ];
};
#define MPTCP_PM_ADDR_MAX 8
......@@ -438,6 +442,41 @@ void mptcp_pm_nl_add_addr_send_ack(struct mptcp_sock *msk)
}
}
int mptcp_pm_nl_mp_prio_send_ack(struct mptcp_sock *msk,
struct mptcp_addr_info *addr,
u8 bkup)
{
struct mptcp_subflow_context *subflow;
pr_debug("bkup=%d", bkup);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
struct sock *sk = (struct sock *)msk;
struct mptcp_addr_info local;
local_address((struct sock_common *)ssk, &local);
if (!addresses_equal(&local, addr, addr->port))
continue;
subflow->backup = bkup;
subflow->send_mp_prio = 1;
subflow->request_bkup = bkup;
__MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPPRIOTX);
spin_unlock_bh(&msk->pm.lock);
pr_debug("send ack for mp_prio");
lock_sock(ssk);
tcp_send_ack(ssk);
release_sock(ssk);
spin_lock_bh(&msk->pm.lock);
return 0;
}
return -EINVAL;
}
void mptcp_pm_nl_rm_addr_received(struct mptcp_sock *msk)
{
struct mptcp_subflow_context *subflow, *tmp;
......@@ -524,10 +563,12 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
/* to keep the code simple, don't do IDR-like allocation for address ID,
* just bail when we exceed limits
*/
if (pernet->next_id > 255)
goto out;
if (pernet->next_id == MAX_ADDR_ID)
pernet->next_id = 1;
if (pernet->addrs >= MPTCP_PM_ADDR_MAX)
goto out;
if (test_bit(entry->addr.id, pernet->id_bitmap))
goto out;
/* do not insert duplicate address, differentiate on port only
* singled addresses
......@@ -539,12 +580,30 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
goto out;
}
if (!entry->addr.id) {
find_next:
entry->addr.id = find_next_zero_bit(pernet->id_bitmap,
MAX_ADDR_ID + 1,
pernet->next_id);
if ((!entry->addr.id || entry->addr.id > MAX_ADDR_ID) &&
pernet->next_id != 1) {
pernet->next_id = 1;
goto find_next;
}
}
if (!entry->addr.id || entry->addr.id > MAX_ADDR_ID)
goto out;
__set_bit(entry->addr.id, pernet->id_bitmap);
if (entry->addr.id > pernet->next_id)
pernet->next_id = entry->addr.id;
if (entry->addr.flags & MPTCP_PM_ADDR_FLAG_SIGNAL)
pernet->add_addr_signal_max++;
if (entry->addr.flags & MPTCP_PM_ADDR_FLAG_SUBFLOW)
pernet->local_addr_max++;
entry->addr.id = pernet->next_id++;
pernet->addrs++;
list_add_tail_rcu(&entry->list, &pernet->local_addr_list);
ret = entry->addr.id;
......@@ -597,6 +656,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)
entry->addr = skc_local;
entry->addr.ifindex = 0;
entry->addr.flags = 0;
entry->addr.id = 0;
ret = mptcp_pm_nl_append_new_local_addr(pernet, entry);
if (ret < 0)
kfree(entry);
......@@ -857,6 +917,7 @@ static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info)
pernet->addrs--;
list_del_rcu(&entry->list);
__clear_bit(entry->addr.id, pernet->id_bitmap);
spin_unlock_bh(&pernet->lock);
mptcp_nl_remove_subflow_and_signal_addr(sock_net(skb->sk), &entry->addr);
......@@ -894,6 +955,8 @@ static int mptcp_nl_cmd_flush_addrs(struct sk_buff *skb, struct genl_info *info)
spin_lock_bh(&pernet->lock);
list_splice_init(&pernet->local_addr_list, &free_list);
__reset_counters(pernet);
pernet->next_id = 1;
bitmap_zero(pernet->id_bitmap, MAX_ADDR_ID + 1);
spin_unlock_bh(&pernet->lock);
__flush_addrs(sock_net(skb->sk), &free_list);
return 0;
......@@ -994,11 +1057,17 @@ static int mptcp_nl_cmd_dump_addrs(struct sk_buff *msg,
struct pm_nl_pernet *pernet;
int id = cb->args[0];
void *hdr;
int i;
pernet = net_generic(net, pm_nl_pernet_id);
spin_lock_bh(&pernet->lock);
list_for_each_entry(entry, &pernet->local_addr_list, list) {
for (i = id; i < MAX_ADDR_ID + 1; i++) {
if (test_bit(i, pernet->id_bitmap)) {
entry = __lookup_addr_by_id(pernet, i);
if (!entry)
break;
if (entry->addr.id <= id)
continue;
......@@ -1016,6 +1085,7 @@ static int mptcp_nl_cmd_dump_addrs(struct sk_buff *msg,
id = entry->addr.id;
genlmsg_end(msg, hdr);
}
}
spin_unlock_bh(&pernet->lock);
cb->args[0] = id;
......@@ -1096,6 +1166,66 @@ mptcp_nl_cmd_get_limits(struct sk_buff *skb, struct genl_info *info)
return -EMSGSIZE;
}
static int mptcp_nl_addr_backup(struct net *net,
struct mptcp_addr_info *addr,
u8 bkup)
{
long s_slot = 0, s_num = 0;
struct mptcp_sock *msk;
int ret = -EINVAL;
while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) {
struct sock *sk = (struct sock *)msk;
if (list_empty(&msk->conn_list))
goto next;
lock_sock(sk);
spin_lock_bh(&msk->pm.lock);
ret = mptcp_pm_nl_mp_prio_send_ack(msk, addr, bkup);
spin_unlock_bh(&msk->pm.lock);
release_sock(sk);
next:
sock_put(sk);
cond_resched();
}
return ret;
}
static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR];
struct pm_nl_pernet *pernet = genl_info_pm_nl(info);
struct mptcp_pm_addr_entry addr, *entry;
struct net *net = sock_net(skb->sk);
u8 bkup = 0;
int ret;
ret = mptcp_pm_parse_addr(attr, info, true, &addr);
if (ret < 0)
return ret;
if (addr.addr.flags & MPTCP_PM_ADDR_FLAG_BACKUP)
bkup = 1;
list_for_each_entry(entry, &pernet->local_addr_list, list) {
if (addresses_equal(&entry->addr, &addr.addr, true)) {
ret = mptcp_nl_addr_backup(net, &entry->addr, bkup);
if (ret)
return ret;
if (bkup)
entry->addr.flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
else
entry->addr.flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP;
}
}
return 0;
}
static const struct genl_small_ops mptcp_pm_ops[] = {
{
.cmd = MPTCP_PM_CMD_ADD_ADDR,
......@@ -1126,6 +1256,11 @@ static const struct genl_small_ops mptcp_pm_ops[] = {
.cmd = MPTCP_PM_CMD_GET_LIMITS,
.doit = mptcp_nl_cmd_get_limits,
},
{
.cmd = MPTCP_PM_CMD_SET_FLAGS,
.doit = mptcp_nl_cmd_set_flags,
.flags = GENL_ADMIN_PERM,
},
};
static struct genl_family mptcp_genl_family __ro_after_init = {
......@@ -1148,6 +1283,7 @@ static int __net_init pm_nl_init_net(struct net *net)
INIT_LIST_HEAD_RCU(&pernet->local_addr_list);
__reset_counters(pernet);
pernet->next_id = 1;
bitmap_zero(pernet->id_bitmap, MAX_ADDR_ID + 1);
spin_lock_init(&pernet->lock);
return 0;
}
......
......@@ -24,6 +24,7 @@
#define OPTION_MPTCP_ADD_ADDR6 BIT(7)
#define OPTION_MPTCP_RM_ADDR BIT(8)
#define OPTION_MPTCP_FASTCLOSE BIT(9)
#define OPTION_MPTCP_PRIO BIT(10)
/* MPTCP option subtypes */
#define MPTCPOPT_MP_CAPABLE 0
......@@ -59,6 +60,7 @@
#define TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT 24
#define TCPOLEN_MPTCP_PORT_LEN 4
#define TCPOLEN_MPTCP_RM_ADDR_BASE 4
#define TCPOLEN_MPTCP_PRIO 4
#define TCPOLEN_MPTCP_FASTCLOSE 12
/* MPTCP MP_JOIN flags */
......@@ -86,6 +88,9 @@
#define MPTCP_ADDR_IPVERSION_4 4
#define MPTCP_ADDR_IPVERSION_6 6
/* MPTCP MP_PRIO flags */
#define MPTCP_PRIO_BKUP BIT(0)
/* MPTCP socket flags */
#define MPTCP_DATA_READY 0
#define MPTCP_NOSPACE 1
......@@ -116,6 +121,7 @@ struct mptcp_options_received {
dss : 1,
add_addr : 1,
rm_addr : 1,
mp_prio : 1,
family : 4,
echo : 1,
backup : 1;
......@@ -396,6 +402,7 @@ struct mptcp_subflow_context {
map_valid : 1,
mpc_map : 1,
backup : 1,
send_mp_prio : 1,
rx_eof : 1,
can_ack : 1, /* only after processing the remote a key */
disposable : 1; /* ctx can be free at ulp release time */
......@@ -550,6 +557,10 @@ void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr);
void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk);
void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id);
void mptcp_pm_mp_prio_received(struct sock *sk, u8 bkup);
int mptcp_pm_nl_mp_prio_send_ack(struct mptcp_sock *msk,
struct mptcp_addr_info *addr,
u8 bkup);
void mptcp_pm_free_anno_list(struct mptcp_sock *msk);
struct mptcp_pm_add_entry *
mptcp_pm_del_add_timer(struct mptcp_sock *msk,
......
......@@ -212,6 +212,7 @@ do_transfer()
rm_nr_ns1="$7"
rm_nr_ns2="$8"
speed="$9"
bkup="${10}"
port=$((10000+$TEST_COUNT))
TEST_COUNT=$((TEST_COUNT+1))
......@@ -297,6 +298,18 @@ do_transfer()
fi
fi
if [ ! -z $bkup ]; then
sleep 1
for netns in "$ns1" "$ns2"; do
dump=(`ip netns exec $netns ./pm_nl_ctl dump`)
if [ ${#dump[@]} -gt 0 ]; then
addr=${dump[${#dump[@]} - 1]}
backup="ip netns exec $netns ./pm_nl_ctl set $addr flags $bkup"
$backup
fi
done
fi
wait $cpid
retc=$?
wait $spid
......@@ -358,6 +371,7 @@ run_tests()
rm_nr_ns1="${5:-0}"
rm_nr_ns2="${6:-0}"
speed="${7:-fast}"
bkup="${8:-""}"
lret=0
oldin=""
......@@ -372,7 +386,7 @@ run_tests()
fi
do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} \
${test_linkfail} ${rm_nr_ns1} ${rm_nr_ns2} ${speed}
${test_linkfail} ${rm_nr_ns1} ${rm_nr_ns2} ${speed} ${bkup}
lret=$?
if [ "$test_linkfail" -eq 1 ];then
......@@ -509,6 +523,43 @@ chk_rm_nr()
fi
}
chk_prio_nr()
{
local mp_prio_nr_tx=$1
local mp_prio_nr_rx=$2
local count
local dump_stats
printf "%-39s %s" " " "ptx"
count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}'`
[ -z "$count" ] && count=0
if [ "$count" != "$mp_prio_nr_tx" ]; then
echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx"
ret=1
dump_stats=1
else
echo -n "[ ok ]"
fi
echo -n " - prx "
count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}'`
[ -z "$count" ] && count=0
if [ "$count" != "$mp_prio_nr_rx" ]; then
echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx"
ret=1
dump_stats=1
else
echo "[ ok ]"
fi
if [ "${dump_stats}" = 1 ]; then
echo Server ns stats
ip netns exec $ns1 nstat -as | grep MPTcp
echo Client ns stats
ip netns exec $ns2 nstat -as | grep MPTcp
fi
}
sin=$(mktemp)
sout=$(mktemp)
cin=$(mktemp)
......@@ -739,6 +790,25 @@ chk_join_nr "remove subflow and signal IPv6" 2 2 2
chk_add_nr 1 1
chk_rm_nr 1 1
# single subflow, backup
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow,backup
run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow nobackup
chk_join_nr "single subflow, backup" 1 1 1
chk_prio_nr 0 1
# single address, backup
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup
chk_join_nr "single address, backup" 1 1 1
chk_add_nr 1 1
chk_prio_nr 1 0
# single subflow, syncookies
reset_with_cookies
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
......
......@@ -91,7 +91,7 @@ id 3 flags signal,backup 10.0.1.3" "dump addrs after del"
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3
check "ip netns exec $ns1 ./pm_nl_ctl get 4" "" "duplicate addr"
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 id 10 flags signal
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 flags signal
check "ip netns exec $ns1 ./pm_nl_ctl get 4" "id 4 flags signal 10.0.1.4" "id addr increment"
for i in `seq 5 9`; do
......@@ -102,9 +102,10 @@ check "ip netns exec $ns1 ./pm_nl_ctl get 10" "" "above hard addr limit"
for i in `seq 9 256`; do
ip netns exec $ns1 ./pm_nl_ctl del $i
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9 id $((i+1))
done
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags 10.0.1.1
id 2 flags 10.0.0.9
id 3 flags signal,backup 10.0.1.3
id 4 flags signal 10.0.1.4
id 5 flags signal 10.0.1.5
......@@ -127,4 +128,40 @@ ip netns exec $ns1 ./pm_nl_ctl limits 8 8
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8
subflows 8" "set limits"
ip netns exec $ns1 ./pm_nl_ctl flush
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3 id 100
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.5 id 254
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.6
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.7
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.8
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags 10.0.1.1
id 2 flags 10.0.1.2
id 3 flags 10.0.1.7
id 4 flags 10.0.1.8
id 100 flags 10.0.1.3
id 101 flags 10.0.1.4
id 254 flags 10.0.1.5
id 255 flags 10.0.1.6" "set ids"
ip netns exec $ns1 ./pm_nl_ctl flush
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.1
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.2 id 254
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.3
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.4
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.5 id 253
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.6
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.7
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.8
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags 10.0.0.1
id 2 flags 10.0.0.4
id 3 flags 10.0.0.6
id 4 flags 10.0.0.7
id 5 flags 10.0.0.8
id 253 flags 10.0.0.5
id 254 flags 10.0.0.2
id 255 flags 10.0.0.3" "wrap-around ids"
exit $ret
......@@ -24,10 +24,11 @@
static void syntax(char *argv[])
{
fprintf(stderr, "%s add|get|del|flush|dump|accept [<args>]\n", argv[0]);
fprintf(stderr, "%s add|get|set|del|flush|dump|accept [<args>]\n", argv[0]);
fprintf(stderr, "\tadd [flags signal|subflow|backup] [id <nr>] [dev <name>] <ip>\n");
fprintf(stderr, "\tdel <id>\n");
fprintf(stderr, "\tget <id>\n");
fprintf(stderr, "\tset <ip> [flags backup|nobackup]\n");
fprintf(stderr, "\tflush\n");
fprintf(stderr, "\tdump\n");
fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
......@@ -584,6 +585,88 @@ int get_set_limits(int fd, int pm_family, int argc, char *argv[])
return 0;
}
int set_flags(int fd, int pm_family, int argc, char *argv[])
{
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
1024];
struct rtattr *rta, *nest;
struct nlmsghdr *nh;
u_int32_t flags = 0;
u_int16_t family;
int nest_start;
int off = 0;
int arg;
memset(data, 0, sizeof(data));
nh = (void *)data;
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SET_FLAGS,
MPTCP_PM_VER);
if (argc < 3)
syntax(argv);
nest_start = off;
nest = (void *)(data + off);
nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
nest->rta_len = RTA_LENGTH(0);
off += NLMSG_ALIGN(nest->rta_len);
/* addr data */
rta = (void *)(data + off);
if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) {
family = AF_INET;
rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
rta->rta_len = RTA_LENGTH(4);
} else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) {
family = AF_INET6;
rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
rta->rta_len = RTA_LENGTH(16);
} else {
error(1, errno, "can't parse ip %s", argv[2]);
}
off += NLMSG_ALIGN(rta->rta_len);
/* family */
rta = (void *)(data + off);
rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
rta->rta_len = RTA_LENGTH(2);
memcpy(RTA_DATA(rta), &family, 2);
off += NLMSG_ALIGN(rta->rta_len);
for (arg = 3; arg < argc; arg++) {
if (!strcmp(argv[arg], "flags")) {
char *tok, *str;
/* flags */
if (++arg >= argc)
error(1, 0, " missing flags value");
/* do not support flag list yet */
for (str = argv[arg]; (tok = strtok(str, ","));
str = NULL) {
if (!strcmp(tok, "backup"))
flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
else if (strcmp(tok, "nobackup"))
error(1, errno,
"unknown flag %s", argv[arg]);
}
rta = (void *)(data + off);
rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &flags, 4);
off += NLMSG_ALIGN(rta->rta_len);
} else {
error(1, 0, "unknown keyword %s", argv[arg]);
}
}
nest->rta_len = off - nest_start;
do_nl_req(fd, nh, off, 0);
return 0;
}
int main(int argc, char *argv[])
{
int fd, pm_family;
......@@ -609,6 +692,8 @@ int main(int argc, char *argv[])
return dump_addrs(fd, pm_family, argc, argv);
else if (!strcmp(argv[1], "limits"))
return get_set_limits(fd, pm_family, argc, argv);
else if (!strcmp(argv[1], "set"))
return set_flags(fd, pm_family, argc, argv);
fprintf(stderr, "unknown sub-command: %s", argv[1]);
syntax(argv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment