|
@@ -1697,6 +1697,7 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
|
|
|
struct sctp_association *asoc;
|
|
|
enum sctp_scope scope;
|
|
|
struct cmsghdr *cmsg;
|
|
|
+ __be32 flowinfo = 0;
|
|
|
struct sctp_af *af;
|
|
|
int err;
|
|
|
|
|
@@ -1781,6 +1782,9 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
|
|
|
if (!cmsgs->addrs_msg)
|
|
|
return 0;
|
|
|
|
|
|
+ if (daddr->sa.sa_family == AF_INET6)
|
|
|
+ flowinfo = daddr->v6.sin6_flowinfo;
|
|
|
+
|
|
|
/* sendv addr list parse */
|
|
|
for_each_cmsghdr(cmsg, cmsgs->addrs_msg) {
|
|
|
struct sctp_transport *transport;
|
|
@@ -1813,6 +1817,7 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
|
|
|
}
|
|
|
|
|
|
dlen = sizeof(struct in6_addr);
|
|
|
+ daddr->v6.sin6_flowinfo = flowinfo;
|
|
|
daddr->v6.sin6_family = AF_INET6;
|
|
|
daddr->v6.sin6_port = htons(asoc->peer.port);
|
|
|
memcpy(&daddr->v6.sin6_addr, CMSG_DATA(cmsg), dlen);
|
|
@@ -2393,6 +2398,8 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
|
|
|
* uint32_t spp_pathmtu;
|
|
|
* uint32_t spp_sackdelay;
|
|
|
* 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
|
|
@@ -2472,6 +2479,45 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
|
|
|
* also that this field is mutually exclusive to
|
|
|
* SPP_SACKDELAY_ENABLE, setting both will have undefined
|
|
|
* 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,
|
|
|
struct sctp_transport *trans,
|
|
@@ -2611,6 +2657,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;
|
|
|
}
|
|
|
|
|
@@ -2625,11 +2716,18 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
|
|
|
int error;
|
|
|
int hb_change, pmtud_change, sackdelay_change;
|
|
|
|
|
|
- if (optlen != sizeof(struct sctp_paddrparams))
|
|
|
+ if (optlen == sizeof(params)) {
|
|
|
+ if (copy_from_user(¶ms, optval, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+ } else if (optlen == ALIGN(offsetof(struct sctp_paddrparams,
|
|
|
+ spp_ipv6_flowlabel), 4)) {
|
|
|
+ if (copy_from_user(¶ms, optval, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+ if (params.spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL))
|
|
|
+ return -EINVAL;
|
|
|
+ } else {
|
|
|
return -EINVAL;
|
|
|
-
|
|
|
- if (copy_from_user(¶ms, optval, optlen))
|
|
|
- return -EFAULT;
|
|
|
+ }
|
|
|
|
|
|
/* Validate flags and value parameters. */
|
|
|
hb_change = params.spp_flags & SPP_HB;
|
|
@@ -5453,6 +5551,45 @@ out:
|
|
|
* also that this field is mutually exclusive to
|
|
|
* SPP_SACKDELAY_ENABLE, setting both will have undefined
|
|
|
* 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,
|
|
|
char __user *optval, int __user *optlen)
|
|
@@ -5462,9 +5599,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
|
|
|
struct sctp_association *asoc = NULL;
|
|
|
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;
|
|
|
- len = sizeof(struct sctp_paddrparams);
|
|
|
+
|
|
|
if (copy_from_user(¶ms, optval, len))
|
|
|
return -EFAULT;
|
|
|
|
|
@@ -5499,6 +5642,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
|
|
|
|
|
|
/*draft-11 doesn't say what to return in spp_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) {
|
|
|
/* Fetch association values. */
|
|
|
params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
|
|
@@ -5508,6 +5660,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
|
|
|
|
|
|
/*draft-11 doesn't say what to return in spp_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 {
|
|
|
/* Fetch socket values. */
|
|
|
params.spp_hbinterval = sp->hbinterval;
|
|
@@ -5517,6 +5678,15 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
|
|
|
|
|
|
/*draft-11 doesn't say what to return in spp_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, ¶ms, len))
|