diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index d5defe9172e3..e84d00bbe9a1 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -136,8 +136,7 @@ static int rds_getname(struct socket *sock, struct sockaddr *uaddr, sin6->sin6_port = rs->rs_conn_port; sin6->sin6_addr = rs->rs_conn_addr; sin6->sin6_flowinfo = 0; - /* scope_id is the same as in the bound address. */ - sin6->sin6_scope_id = rs->rs_bound_scope_id; + sin6->sin6_scope_id = rs->rs_conn_scope_id; uaddr_len = sizeof(*sin6); } } else { @@ -572,6 +571,7 @@ static int rds_connect(struct socket *sock, struct sockaddr_unsized *uaddr, } ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr); rs->rs_conn_port = sin->sin_port; + rs->rs_conn_scope_id = 0; break; #if IS_ENABLED(CONFIG_IPV6) @@ -616,11 +616,14 @@ static int rds_connect(struct socket *sock, struct sockaddr_unsized *uaddr, ret = -EINVAL; break; } - /* Remember the connected address scope ID. It will - * be checked against the binding local address when - * the socket is bound. + /* Remember the connected address scope ID. It is + * checked against the binding local address when + * the socket is bound, and gives a send without a + * destination its scope. */ - rs->rs_bound_scope_id = sin6->sin6_scope_id; + rs->rs_conn_scope_id = sin6->sin6_scope_id; + } else { + rs->rs_conn_scope_id = 0; } rs->rs_conn_addr = sin6->sin6_addr; rs->rs_conn_port = sin6->sin6_port; diff --git a/net/rds/bind.c b/net/rds/bind.c index f800d920d969..3ac59cd512a2 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -233,8 +233,8 @@ int rds_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) * non-link local address (scope_id is 0). */ if (!ipv6_addr_any(&rs->rs_conn_addr) && scope_id && - rs->rs_bound_scope_id && - scope_id != rs->rs_bound_scope_id) { + rs->rs_conn_scope_id && + scope_id != rs->rs_conn_scope_id) { ret = -EINVAL; goto out; } diff --git a/net/rds/rds.h b/net/rds/rds.h index 2db49573dacd..9b1ffc49c0a6 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -646,6 +646,8 @@ struct rds_sock { struct in6_addr rs_conn_addr; #define rs_conn_addr_v4 rs_conn_addr.s6_addr32[3] __be16 rs_conn_port; + /* scope of rs_conn_addr when it is link-local, 0 otherwise */ + __u32 rs_conn_scope_id; struct rds_transport *rs_transport; /* diff --git a/net/rds/recv.c b/net/rds/recv.c index 93ee4546ae36..00ab60f2d996 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -791,7 +791,16 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, sin6->sin6_port = inc->i_hdr.h_sport; sin6->sin6_addr = inc->i_saddr; sin6->sin6_flowinfo = 0; - sin6->sin6_scope_id = rs->rs_bound_scope_id; + /* A link-local sender is reported with the + * interface recorded on its connection (the + * accepting socket's device for TCP, the one + * resolved for the local address on IB); any + * other sender has no scope. + */ + sin6->sin6_scope_id = + (ipv6_addr_type(&inc->i_saddr) & + IPV6_ADDR_LINKLOCAL) ? + inc->i_conn->c_dev_if : 0; msg->msg_namelen = sizeof(*sin6); } } diff --git a/net/rds/send.c b/net/rds/send.c index 38b9cc9e0d20..f38967f65af1 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1256,7 +1256,11 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) lock_sock(sk); daddr = rs->rs_conn_addr; dport = rs->rs_conn_port; - scope_id = rs->rs_bound_scope_id; + /* The connected peer's scope for a link-local peer, else + * the bound scope, as an explicit send to that peer would + * compute below. + */ + scope_id = rs->rs_conn_scope_id ?: rs->rs_bound_scope_id; release_sock(sk); }