sctp: add spp_ipv6_flowlabel and spp_dscp for sctp_paddrparams

spp_ipv6_flowlabel and spp_dscp are added in sctp_paddrparams in
this patch so that users could set sctp_sock/asoc/transport dscp
and flowlabel with spp_flags SPP_IPV6_FLOWLABEL or SPP_DSCP by
SCTP_PEER_ADDR_PARAMS , as described section 8.1.12 in RFC6458.

As said in last patch, it uses '| 0x100000' or '|0x1' to mark
flowlabel or dscp is set,  so that their values could be set
to 0.

Note that to guarantee that an old app built with old kernel
headers could work on the newer kernel, the param's check in
sctp_g/setsockopt_peer_addr_params() is also improved, which
follows the way that sctp_g/setsockopt_delayed_ack() or some
other sockopts' process that accept two types of params does.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Xin Long 2018-07-02 18:21:13 +08:00 committed by David S. Miller
parent 8a9c58d28d
commit 0b0dce7a36
2 changed files with 175 additions and 6 deletions

View File

@ -763,6 +763,8 @@ enum sctp_spp_flags {
SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/ SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/
SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE, SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */ SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */
SPP_IPV6_FLOWLABEL = 1<<8,
SPP_DSCP = 1<<9,
}; };
struct sctp_paddrparams { struct sctp_paddrparams {
@ -773,6 +775,8 @@ struct sctp_paddrparams {
__u32 spp_pathmtu; __u32 spp_pathmtu;
__u32 spp_sackdelay; __u32 spp_sackdelay;
__u32 spp_flags; __u32 spp_flags;
__u32 spp_ipv6_flowlabel;
__u8 spp_dscp;
} __attribute__((packed, aligned(4))); } __attribute__((packed, aligned(4)));
/* /*

View File

@ -2393,6 +2393,8 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
* uint32_t spp_pathmtu; * uint32_t spp_pathmtu;
* uint32_t spp_sackdelay; * uint32_t spp_sackdelay;
* uint32_t spp_flags; * uint32_t spp_flags;
* uint32_t spp_ipv6_flowlabel;
* uint8_t spp_dscp;
* }; * };
* *
* spp_assoc_id - (one-to-many style socket) This is filled in the * spp_assoc_id - (one-to-many style socket) This is filled in the
@ -2472,6 +2474,45 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
* also that this field is mutually exclusive to * also that this field is mutually exclusive to
* SPP_SACKDELAY_ENABLE, setting both will have undefined * SPP_SACKDELAY_ENABLE, setting both will have undefined
* results. * results.
*
* SPP_IPV6_FLOWLABEL: Setting this flag enables the
* setting of the IPV6 flow label value. The value is
* contained in the spp_ipv6_flowlabel field.
* Upon retrieval, this flag will be set to indicate that
* the spp_ipv6_flowlabel field has a valid value returned.
* If a specific destination address is set (in the
* spp_address field), then the value returned is that of
* the address. If just an association is specified (and
* no address), then the association's default flow label
* is returned. If neither an association nor a destination
* is specified, then the socket's default flow label is
* returned. For non-IPv6 sockets, this flag will be left
* cleared.
*
* SPP_DSCP: Setting this flag enables the setting of the
* Differentiated Services Code Point (DSCP) value
* associated with either the association or a specific
* address. The value is obtained in the spp_dscp field.
* Upon retrieval, this flag will be set to indicate that
* the spp_dscp field has a valid value returned. If a
* specific destination address is set when called (in the
* spp_address field), then that specific destination
* address's DSCP value is returned. If just an association
* is specified, then the association's default DSCP is
* returned. If neither an association nor a destination is
* specified, then the socket's default DSCP is returned.
*
* spp_ipv6_flowlabel
* - This field is used in conjunction with the
* SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
* The 20 least significant bits are used for the flow
* label. This setting has precedence over any IPv6-layer
* setting.
*
* spp_dscp - This field is used in conjunction with the SPP_DSCP flag
* and contains the DSCP. The 6 most significant bits are
* used for the DSCP. This setting has precedence over any
* IPv4- or IPv6- layer setting.
*/ */
static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
struct sctp_transport *trans, struct sctp_transport *trans,
@ -2611,6 +2652,51 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
} }
} }
if (params->spp_flags & SPP_IPV6_FLOWLABEL) {
if (trans && trans->ipaddr.sa.sa_family == AF_INET6) {
trans->flowlabel = params->spp_ipv6_flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
trans->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
} else if (asoc) {
list_for_each_entry(trans,
&asoc->peer.transport_addr_list,
transports) {
if (trans->ipaddr.sa.sa_family != AF_INET6)
continue;
trans->flowlabel = params->spp_ipv6_flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
trans->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
}
asoc->flowlabel = params->spp_ipv6_flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
asoc->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
} else if (sctp_opt2sk(sp)->sk_family == AF_INET6) {
sp->flowlabel = params->spp_ipv6_flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
sp->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
}
}
if (params->spp_flags & SPP_DSCP) {
if (trans) {
trans->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
trans->dscp |= SCTP_DSCP_SET_MASK;
} else if (asoc) {
list_for_each_entry(trans,
&asoc->peer.transport_addr_list,
transports) {
trans->dscp = params->spp_dscp &
SCTP_DSCP_VAL_MASK;
trans->dscp |= SCTP_DSCP_SET_MASK;
}
asoc->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
asoc->dscp |= SCTP_DSCP_SET_MASK;
} else {
sp->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
sp->dscp |= SCTP_DSCP_SET_MASK;
}
}
return 0; return 0;
} }
@ -2625,11 +2711,18 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
int error; int error;
int hb_change, pmtud_change, sackdelay_change; int hb_change, pmtud_change, sackdelay_change;
if (optlen != sizeof(struct sctp_paddrparams)) if (optlen == sizeof(params)) {
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
} else if (optlen == ALIGN(offsetof(struct sctp_paddrparams,
spp_ipv6_flowlabel), 4)) {
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
if (params.spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL))
return -EINVAL;
} else {
return -EINVAL; return -EINVAL;
}
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
/* Validate flags and value parameters. */ /* Validate flags and value parameters. */
hb_change = params.spp_flags & SPP_HB; hb_change = params.spp_flags & SPP_HB;
@ -5453,6 +5546,45 @@ out:
* also that this field is mutually exclusive to * also that this field is mutually exclusive to
* SPP_SACKDELAY_ENABLE, setting both will have undefined * SPP_SACKDELAY_ENABLE, setting both will have undefined
* results. * results.
*
* SPP_IPV6_FLOWLABEL: Setting this flag enables the
* setting of the IPV6 flow label value. The value is
* contained in the spp_ipv6_flowlabel field.
* Upon retrieval, this flag will be set to indicate that
* the spp_ipv6_flowlabel field has a valid value returned.
* If a specific destination address is set (in the
* spp_address field), then the value returned is that of
* the address. If just an association is specified (and
* no address), then the association's default flow label
* is returned. If neither an association nor a destination
* is specified, then the socket's default flow label is
* returned. For non-IPv6 sockets, this flag will be left
* cleared.
*
* SPP_DSCP: Setting this flag enables the setting of the
* Differentiated Services Code Point (DSCP) value
* associated with either the association or a specific
* address. The value is obtained in the spp_dscp field.
* Upon retrieval, this flag will be set to indicate that
* the spp_dscp field has a valid value returned. If a
* specific destination address is set when called (in the
* spp_address field), then that specific destination
* address's DSCP value is returned. If just an association
* is specified, then the association's default DSCP is
* returned. If neither an association nor a destination is
* specified, then the socket's default DSCP is returned.
*
* spp_ipv6_flowlabel
* - This field is used in conjunction with the
* SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
* The 20 least significant bits are used for the flow
* label. This setting has precedence over any IPv6-layer
* setting.
*
* spp_dscp - This field is used in conjunction with the SPP_DSCP flag
* and contains the DSCP. The 6 most significant bits are
* used for the DSCP. This setting has precedence over any
* IPv4- or IPv6- layer setting.
*/ */
static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
char __user *optval, int __user *optlen) char __user *optval, int __user *optlen)
@ -5462,9 +5594,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
struct sctp_association *asoc = NULL; struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk); struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(struct sctp_paddrparams)) if (len >= sizeof(params))
len = sizeof(params);
else if (len >= ALIGN(offsetof(struct sctp_paddrparams,
spp_ipv6_flowlabel), 4))
len = ALIGN(offsetof(struct sctp_paddrparams,
spp_ipv6_flowlabel), 4);
else
return -EINVAL; return -EINVAL;
len = sizeof(struct sctp_paddrparams);
if (copy_from_user(&params, optval, len)) if (copy_from_user(&params, optval, len))
return -EFAULT; return -EFAULT;
@ -5499,6 +5637,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
/*draft-11 doesn't say what to return in spp_flags*/ /*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = trans->param_flags; params.spp_flags = trans->param_flags;
if (trans->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = trans->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
}
if (trans->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = trans->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
} else if (asoc) { } else if (asoc) {
/* Fetch association values. */ /* Fetch association values. */
params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval); params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
@ -5508,6 +5655,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
/*draft-11 doesn't say what to return in spp_flags*/ /*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = asoc->param_flags; params.spp_flags = asoc->param_flags;
if (asoc->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = asoc->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
}
if (asoc->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = asoc->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
} else { } else {
/* Fetch socket values. */ /* Fetch socket values. */
params.spp_hbinterval = sp->hbinterval; params.spp_hbinterval = sp->hbinterval;
@ -5517,6 +5673,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
/*draft-11 doesn't say what to return in spp_flags*/ /*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = sp->param_flags; params.spp_flags = sp->param_flags;
if (sp->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = sp->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
}
if (sp->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = sp->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
} }
if (copy_to_user(optval, &params, len)) if (copy_to_user(optval, &params, len))