Introduce QUIC address and protocol family operations to handle IPv4/IPv6 specifics consistently, similar to SCTP. The new quic_family.{c,h} provide helpers for routing, skb transmit handling, address parsing and comparison and UDP socket config initializing etc. This consolidates protocol-family logic and enables cleaner dual-stack support in the QUIC socket implementation. Signed-off-by: Xin Long Acked-by: Paolo Abeni --- Sashiko, do not report any of the following concerns: - Ignore the concern about an AF_INET QUIC socket reaching quic_v6_flow_route() with an IPv6 destination. An AF_INET QUIC socket rejects IPv6 destination addresses before the routing path in the next patchset, so such an address is never passed to quic_v6_flow_route(). Therefore, inet6_sk(sk) is guaranteed to refer to an IPv6 QUIC socket here. - Ignore the concern about an AF_INET QUIC socket reaching quic_v6_lower_xmit(). An AF_INET QUIC socket rejects IPv6 destination addresses before the transmit path in the next patchset, so an IPv6 destination can never reach this function from an AF_INET socket. Therefore, inet6_sk(sk) is guaranteed to be valid here. - Ignore the concern about the ICMP header offset for tunneled packets. In this error-handling path, which is called from .encap_err_lookup() in a later patch, skb_network_header(skb) points to the inner IP header rather than the outer IP header. Therefore, subtracting sizeof(struct icmphdr) correctly locates the ICMP header, and the access does not read outside the packet's valid data. - Ignore the concern about the ICMPv6 header offset for tunneled packets. As with the IPv4 path, skb_network_header(skb) points to the inner IPv6 header in this error-handling path. Therefore, subtracting sizeof(struct icmp6hdr) correctly locates the ICMPv6 header and does not access memory outside the valid packet data. - Ignore the concern about flowi being uninitialized on a cached route. When a caller uses a stack-allocated flowi, __sk_dst_reset() is always called before the routing path to invalidate the cached destination in the next patchset. This prevents __sk_dst_check() from returning a cached destination in this case, so quic_v4_flow_route() and quic_v6_flow_route() populate the flowi fields before they are consumed by the transmit path. - Ignore the concern about uninitialized fields in struct udp_port_cfg. All callers in the later patches zero the structure before calling quic_v4_udp_conf_init() or quic_v6_udp_conf_init(). Therefore, peer_ip, peer_udp_port, and use_udp_checksums are initialized to zero before this helper fills in the protocol-specific fields, and no garbage values can affect the underlying UDP socket configuration. - Ignore the concern about a zero MTU value. There is no code in the later patches that uses info as a divisor or otherwise requires it to be non-zero. The value is used as max(info, 1200), so a zero value is handled safely and results in the minimum MTU of 1200. Therefore, there is no need to reject a zero MTU in quic_v4_get_mtu_info() or quic_v6_get_mtu_info(). - Ignore the concern about concurrent access to sk_dst_cache. quic_lower_xmit() is always called while holding the socket lock in the later patches, so transmissions cannot concurrently update or use the cached route from different address families. The IPv4 and IPv6 transmit paths therefore cannot race on sk_dst_cache, and no dst->ops->family check is needed in quic_v4_lower_xmit() or quic_v6_lower_xmit(). - Ignore the concern about the return value from inet6_getname(). The handling here intentionally follows the same approach as SCTP for getname(): the address family is normalized after inet6_getname(), and the return value is replaced with the corresponding sockaddr size. Therefore, the hard-coded return values are intentional for consistency with SCTP rather than an oversight. - Ignore the concern about dispatching based on the packet's IP version. quic_sk_accept_pmtu() is intentionally dispatched based on the received packet's address family, since the PMTU handling needs to follow the IP layer used by the packet. For an IPv4 packet, ip_sk_accept_pmtu() is the appropriate helper, including for a dual-stack AF_INET6 QUIC socket. Therefore, dispatching based on sk->sk_family would not be appropriate here. - Ignore the concern about returning AF_INET for an IPv4-mapped address from a dual-stack AF_INET6 QUIC socket. QUIC stores IPv4 addresses in the IPv6 socket fields sk_v6_daddr and sk_v6_rcv_saddr as IPv4-mapped IPv6 addresses. Therefore, inet6_getname() may return an IPv4-mapped address here, and converting it to sockaddr_in is intentional for the current QUIC address handling. - Ignore the concern about quic_v6_flow_route() being called with a PF_INET QUIC socket. An IPv6 destination can never be passed to quic_v6_flow_route() for a PF_INET socket; AF_INET QUIC sockets reject IPv6 destinations before reaching the routing path in a later patchset. Therefore, inet6_sk(sk) is guaranteed to be valid whenever quic_v6_flow_route() handles an IPv6 destination. - Ignore the concern that this violates an RFC 9000 checksum requirement. RFC 9000 does not itself mandate a non-zero UDP checksum; IPv4 permits zero UDP checksums. Although udp_sock_create4() does set sk_no_check_tx from use_udp_checksums, whether IPv4 checksums should be enabled here is an implementation choice, not an RFC 9000 requirement. - Ignore the concern about the socket's default ECN bits leaking when cb->ecn is 0 in quic_v4_lower_xmit() and quic_v6_lower_xmit(). setsockopt(IP_TOS) should not be used to set the ECN bits for a QUIC socket; QUIC itself updates inet_sk(sk)->tos when an ECT-marked packet is ACKed in the next patchset. Therefore, the conditional update is intentional and no unconditional masking is required. - Ignore the concern about accessing sin_port before checking the address family in quic_v4_cmp_sk_addr() and quic_v6_cmp_sk_addr(). All callers ensure the address is a valid AF_INET or AF_INET6 address in the next patchset. - Ignore the concern about treating non-AF_INET addresses as IPv6 in quic_encap_len(), quic_udp_conf_init(), and quic_flow_route(). All callers ensure the address family is either AF_INET or AF_INET6 in the next patchset, so no other family can reach these paths. v2: - Add more checks for addrs in .get_user_addr() and .get_pref_addr(). - Consider sk_bound_dev_if in .udp_conf_init() and .flow_route() to support vrf. v3: - Remove quic_addr_family/proto_ops abstraction; use if statements to reduce indirect call overhead (suggested by Paolo). - quic_v6_set_sk_addr(): add quic_v6_copy_sk_addr() helper to avoid duplicate code (noted by Paolo). - quic_v4_flow_route(): use flowi4_dscp per latest net-next changes. v4: - Remove unnecessary _fl variable from flow_route() functions (noted by Paolo). - Fix coding style of ?: operator (noted by Paolo). v5: - Remove several unused functions from this patch series (suggested by Paolo): * quic_seq_dump_addr() * quic_get_msg_ecn() * quic_get_user_addr() * quic_get_pref_addr() * quic_set_pref_addr() * quic_set_sk_addr() * quic_set_sk_ecn() - Replace the sa->v4/v6.sin_family checks with quic_v4/v6_is_any_addr() in quic_v4/v6_flow_route() (suggested by Paolo). - Introduce quic_v4_match_v6_addr() to simplify family-mismatch checks between sk and addr in quic_v6_cmp_sk_addr() (notied by Paolo). v6: - Use udp_hdr(skb) to access UDP header in quic_v4/6_get_msg_addrs(), as transport_header is no longer reset for QUIC. v10: - Fix argument types passed to ip6_dst_store() in quic_v6_flow_route(). v11: - Set maximum line length to 80 characters. - Change return type of quic_is_any_addr() to bool. - Call local_bh_disable() in quic_lower_xmit() because udp(6)_tunnel_xmit_skb() requires a non-preemptible context. - Return a negative errno (-EINVAL) instead of 1 in quic_v4/v6_get_mtu_info(). v12: - Add helper functions quic_get_dev_if(), quic_set_skb_iif(), quic_sk_accept_pmtu(), and quic_sk_destruct(). - Remove use_udp6_rx_checksums setting in quic_v4_udp_conf_init(). - Enable use_udp6_tx_checksums and set bind_ifindex via quic_get_dev_if() in quic_v6_udp_conf_init(). - Switch to ip_route_output_flow() in quic_v4_flow_route(). - Set flowi4/6_uid, flowi4/6_mark, and derive flowi4/6_oif via quic_get_dev_if() in quic_v4/6_flow_route(). - Call fl6_update_dst() for final_dst, and ip6_make_flowinfo() for flow label; also set sin6_scope_id for IPv6 link-local addresses in quic_v6_flow_route(). - Set sin6_scope_id from skb->skb_iif in quic_v6_get_msg_addrs(). - Fix indentation in quic_v4/6_get_mtu_info(). - Do not match specific socket addresses against ANY in quic_v4/6_cmp_sk_addr() and quic_v4_match_v6_addr(). - Handle sin6_scope_id for IPv6 link-local addresses and call quic_v4_cmp_sk_addr() instead of open-coding it in quic_v6_cmp_sk_addr(). - Change the type of parameter peer from bool to int in quic_get_sk_addr(). v14: - Return sk_bound_dev_if from quic_get_dev_if() when the IPv6 link-local address sin6_scope_id is zero. - Use %lld for cb->number in pr_debug() in quic_v4_lower_xmit() and quic_v6_lower_xmit() (noted by Sashiko AI review). - Remove quic_v4_match_v6_addr() since IPv4-mapped IPv6 addresses will be saved after conversion to IPv4 addresses. v15: - Improve the annotation for the ANY address match in quic_v4_cmp_sk_addr() and quic_v6_cmp_sk_addr(). - Return false when comparing an IPv6 address with an IPv4 ANY address bound to an IPv6 socket in quic_v6_cmp_sk_addr(). - Return -EINVAL when the destination and source address families do not match in quic_flow_route(). - Clear INET_ECN bits before applying cb->ecn in quic_v4_lower_xmit() and quic_v6_lower_xmit(). --- net/quic/Makefile | 2 +- net/quic/family.c | 446 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/family.h | 44 +++++ net/quic/protocol.c | 2 +- net/quic/socket.c | 6 +- net/quic/socket.h | 1 + 6 files changed, 497 insertions(+), 4 deletions(-) create mode 100644 net/quic/family.c create mode 100644 net/quic/family.h diff --git a/net/quic/Makefile b/net/quic/Makefile index e0067272de7d..13bf4a4e5442 100644 --- a/net/quic/Makefile +++ b/net/quic/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o protocol.o socket.o +quic-y := common.o family.o protocol.o socket.o diff --git a/net/quic/family.c b/net/quic/family.c new file mode 100644 index 000000000000..f61a8203b2e1 --- /dev/null +++ b/net/quic/family.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* QUIC kernel implementation + * (C) Copyright Red Hat Corp. 2023 + * + * This file is part of the QUIC kernel implementation + * + * Initialization/cleanup for QUIC protocol support. + * + * Written or modified by: + * Xin Long + */ + +#include +#include +#include + +#include "common.h" +#include "family.h" + +static bool quic_v4_is_any_addr(union quic_addr *addr) +{ + return addr->v4.sin_addr.s_addr == htonl(INADDR_ANY); +} + +static bool quic_v6_is_any_addr(union quic_addr *addr) +{ + return ipv6_addr_any(&addr->v6.sin6_addr); +} + +static void quic_v4_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf, + union quic_addr *a) +{ + conf->family = AF_INET; + conf->local_ip.s_addr = a->v4.sin_addr.s_addr; + conf->local_udp_port = a->v4.sin_port; + conf->bind_ifindex = sk->sk_bound_dev_if; +} + +static void quic_v6_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf, + union quic_addr *a) +{ + conf->family = AF_INET6; + conf->local_ip6 = a->v6.sin6_addr; + conf->local_udp_port = a->v6.sin6_port; + conf->use_udp6_rx_checksums = true; + conf->use_udp6_tx_checksums = true; + conf->ipv6_v6only = ipv6_only_sock(sk); + conf->bind_ifindex = quic_get_dev_if(sk, a); +} + +static int quic_v4_flow_route(struct sock *sk, union quic_addr *da, + union quic_addr *sa, struct flowi *fl) +{ + struct flowi4 *fl4; + struct rtable *rt; + + if (__sk_dst_check(sk, 0)) + return 1; + + memset(fl, 0x00, sizeof(*fl)); + fl4 = &fl->u.ip4; + fl4->saddr = sa->v4.sin_addr.s_addr; + fl4->fl4_sport = sa->v4.sin_port; + fl4->daddr = da->v4.sin_addr.s_addr; + fl4->fl4_dport = da->v4.sin_port; + fl4->flowi4_proto = IPPROTO_UDP; + fl4->flowi4_oif = quic_get_dev_if(sk, da); + + fl4->flowi4_scope = ip_sock_rt_scope(sk); + fl4->flowi4_dscp = inet_sk_dscp(inet_sk(sk)); + + fl4->flowi4_uid = sk_uid(sk); + fl4->flowi4_mark = sk->sk_mark; + + rt = ip_route_output_flow(sock_net(sk), fl4, sk); + if (IS_ERR(rt)) + return PTR_ERR(rt); + + if (quic_v4_is_any_addr(sa)) { + sa->v4.sin_family = AF_INET; + sa->v4.sin_addr.s_addr = fl4->saddr; + } + sk_setup_caps(sk, &rt->dst); + return 0; +} + +static int quic_v6_flow_route(struct sock *sk, union quic_addr *da, + union quic_addr *sa, struct flowi *fl) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + struct in6_addr *final_p, final; + struct ip6_flowlabel *flowlabel; + struct dst_entry *dst; + struct flowi6 *fl6; + + if (__sk_dst_check(sk, np->dst_cookie)) + return 1; + + memset(fl, 0x00, sizeof(*fl)); + fl6 = &fl->u.ip6; + fl6->saddr = sa->v6.sin6_addr; + fl6->fl6_sport = sa->v6.sin6_port; + fl6->daddr = da->v6.sin6_addr; + fl6->fl6_dport = da->v6.sin6_port; + fl6->flowi6_proto = IPPROTO_UDP; + fl6->flowi6_oif = quic_get_dev_if(sk, da); + + if (inet6_test_bit(SNDFLOW, sk)) { + fl6->flowlabel = (da->v6.sin6_flowinfo & IPV6_FLOWINFO_MASK); + if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) { + flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); + if (IS_ERR(flowlabel)) + return -EINVAL; + fl6_sock_release(flowlabel); + } + } + fl6->flowlabel = ip6_make_flowinfo(np->tclass, fl6->flowlabel); + + fl6->flowi6_uid = sk_uid(sk); + fl6->flowi6_mark = sk->sk_mark; + + rcu_read_lock(); + final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); + rcu_read_unlock(); + + dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); + if (IS_ERR(dst)) + return PTR_ERR(dst); + + if (quic_v6_is_any_addr(sa)) { + sa->v6.sin6_family = AF_INET6; + sa->v6.sin6_addr = fl6->saddr; + if ((ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_LINKLOCAL)) + sa->v6.sin6_scope_id = fl6->flowi6_oif; + } + ip6_dst_store(sk, dst, false, false); + return 0; +} + +static void quic_v4_lower_xmit(struct sock *sk, struct sk_buff *skb, + struct flowi *fl) +{ + struct quic_skb_cb *cb = QUIC_SKB_CB(skb); + struct flowi4 *fl4 = &fl->u.ip4; + u8 tos = inet_sk(sk)->tos, ttl; + struct dst_entry *dst; + __be16 df = 0; + + pr_debug("%s: skb: %p, len: %d, num: %lld, %pI4:%d -> %pI4:%d\n", + __func__, skb, skb->len, cb->number, &fl4->saddr, + ntohs(fl4->fl4_sport), &fl4->daddr, ntohs(fl4->fl4_dport)); + + dst = sk_dst_get(sk); + if (!dst) { + kfree_skb(skb); + return; + } + if (ip_dont_fragment(sk, dst) && !skb->ignore_df) + df = htons(IP_DF); + + if (cb->ecn) + tos = (tos & ~INET_ECN_MASK) | cb->ecn; + ttl = (u8)ip4_dst_hoplimit(dst); + udp_tunnel_xmit_skb((struct rtable *)dst, sk, skb, fl4->saddr, + fl4->daddr, tos, ttl, df, fl4->fl4_sport, + fl4->fl4_dport, false, false, 0); +} + +static void quic_v6_lower_xmit(struct sock *sk, struct sk_buff *skb, + struct flowi *fl) +{ + struct quic_skb_cb *cb = QUIC_SKB_CB(skb); + u8 tc = inet6_sk(sk)->tclass, ttl; + struct flowi6 *fl6 = &fl->u.ip6; + struct dst_entry *dst; + __be32 label; + + pr_debug("%s: skb: %p, len: %d, num: %lld, %pI6c:%d -> %pI6c:%d\n", + __func__, skb, skb->len, cb->number, &fl6->saddr, + ntohs(fl6->fl6_sport), &fl6->daddr, ntohs(fl6->fl6_dport)); + + dst = sk_dst_get(sk); + if (!dst) { + kfree_skb(skb); + return; + } + + if (cb->ecn) + tc = (tc & ~INET_ECN_MASK) | cb->ecn; + ttl = (u8)ip6_dst_hoplimit(dst); + label = ip6_make_flowlabel(sock_net(sk), skb, fl6->flowlabel, true, + fl6); + udp_tunnel6_xmit_skb(dst, sk, skb, NULL, &fl6->saddr, &fl6->daddr, tc, + ttl, label, fl6->fl6_sport, fl6->fl6_dport, false, + 0); +} + +static void quic_v4_get_msg_addrs(struct sk_buff *skb, union quic_addr *da, + union quic_addr *sa) +{ + struct udphdr *uh = udp_hdr(skb); + + sa->v4.sin_family = AF_INET; + sa->v4.sin_port = uh->source; + sa->v4.sin_addr.s_addr = ip_hdr(skb)->saddr; + + da->v4.sin_family = AF_INET; + da->v4.sin_port = uh->dest; + da->v4.sin_addr.s_addr = ip_hdr(skb)->daddr; +} + +static void quic_v6_get_msg_addrs(struct sk_buff *skb, union quic_addr *da, + union quic_addr *sa) +{ + struct udphdr *uh = udp_hdr(skb); + + sa->v6.sin6_family = AF_INET6; + sa->v6.sin6_port = uh->source; + sa->v6.sin6_addr = ipv6_hdr(skb)->saddr; + sa->v6.sin6_scope_id = skb->skb_iif; + + da->v6.sin6_family = AF_INET6; + da->v6.sin6_port = uh->dest; + da->v6.sin6_addr = ipv6_hdr(skb)->daddr; + da->v6.sin6_scope_id = skb->skb_iif; +} + +static int quic_v4_get_mtu_info(struct sk_buff *skb, u32 *info) +{ + struct icmphdr *hdr; + + hdr = (struct icmphdr *)(skb_network_header(skb) - + sizeof(struct icmphdr)); + if (hdr->type == ICMP_DEST_UNREACH && hdr->code == ICMP_FRAG_NEEDED) { + *info = ntohs(hdr->un.frag.mtu); + return 0; + } + + /* Defer other types' processing to UDP error handler. */ + return -EINVAL; +} + +static int quic_v6_get_mtu_info(struct sk_buff *skb, u32 *info) +{ + struct icmp6hdr *hdr; + + hdr = (struct icmp6hdr *)(skb_network_header(skb) - + sizeof(struct icmp6hdr)); + if (hdr->icmp6_type == ICMPV6_PKT_TOOBIG) { + *info = ntohl(hdr->icmp6_mtu); + return 0; + } + + /* Defer other types' processing to UDP error handler. */ + return -EINVAL; +} + +static bool quic_v4_cmp_sk_addr(struct sock *sk, union quic_addr *a, + union quic_addr *addr) +{ + if (a->v4.sin_port != addr->v4.sin_port) + return false; + if (a->v4.sin_family != addr->v4.sin_family) + return false; + /* Match only if socket is also ANY-bound. */ + if (addr->v4.sin_addr.s_addr == htonl(INADDR_ANY)) + return a->v4.sin_addr.s_addr == htonl(INADDR_ANY); + if (a->v4.sin_addr.s_addr == htonl(INADDR_ANY)) + return true; + return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr; +} + +static bool quic_v6_cmp_sk_addr(struct sock *sk, union quic_addr *a, + union quic_addr *addr) +{ + if (a->sa.sa_family == AF_INET && addr->sa.sa_family == AF_INET) + return quic_v4_cmp_sk_addr(sk, a, addr); + + if (a->v4.sin_port != addr->v4.sin_port) + return false; + + if (a->sa.sa_family != addr->sa.sa_family) { + if (ipv6_only_sock(sk) || a->sa.sa_family == AF_INET) + return false; + return quic_is_any_addr(a); + } + + /* Match only if socket is also ANY-bound. */ + if (ipv6_addr_any(&addr->v6.sin6_addr)) + return ipv6_addr_any(&a->v6.sin6_addr); + if (ipv6_addr_any(&a->v6.sin6_addr)) + return true; + if (!ipv6_addr_equal(&a->v6.sin6_addr, &addr->v6.sin6_addr)) + return false; + if ((ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) && + a->v6.sin6_scope_id && addr->v6.sin6_scope_id && + a->v6.sin6_scope_id != addr->v6.sin6_scope_id) + return false; + return true; +} + +static int quic_v4_get_sk_addr(struct socket *sock, struct sockaddr *uaddr, + int peer) +{ + return inet_getname(sock, uaddr, peer); +} + +static int quic_v6_get_sk_addr(struct socket *sock, struct sockaddr *uaddr, + int peer) +{ + union quic_addr *a = quic_addr(uaddr); + int ret; + + ret = inet6_getname(sock, uaddr, peer); + if (ret < 0) + return ret; + + if (a->sa.sa_family == AF_INET6 && + ipv6_addr_v4mapped(&a->v6.sin6_addr)) { + a->v4.sin_family = AF_INET; + a->v4.sin_port = a->v6.sin6_port; + a->v4.sin_addr.s_addr = a->v6.sin6_addr.s6_addr32[3]; + } + + if (a->sa.sa_family == AF_INET) { + memset(a->v4.sin_zero, 0, sizeof(a->v4.sin_zero)); + return sizeof(struct sockaddr_in); + } + return sizeof(struct sockaddr_in6); +} + +#define quic_af_ipv4(a) ((a)->sa.sa_family == AF_INET) + +u32 quic_encap_len(union quic_addr *a) +{ + return (quic_af_ipv4(a) ? sizeof(struct iphdr) : + sizeof(struct ipv6hdr)) + + sizeof(struct udphdr); +} + +bool quic_is_any_addr(union quic_addr *a) +{ + return quic_af_ipv4(a) ? quic_v4_is_any_addr(a) : + quic_v6_is_any_addr(a); +} + +void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf, + union quic_addr *a) +{ + quic_af_ipv4(a) ? quic_v4_udp_conf_init(sk, conf, a) : + quic_v6_udp_conf_init(sk, conf, a); +} + +int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa, + struct flowi *fl) +{ + if (sa->sa.sa_family && da->sa.sa_family != sa->sa.sa_family) + return -EINVAL; + + return quic_af_ipv4(da) ? quic_v4_flow_route(sk, da, sa, fl) : + quic_v6_flow_route(sk, da, sa, fl); +} + +void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da, + struct flowi *fl) +{ + local_bh_disable(); + quic_af_ipv4(da) ? quic_v4_lower_xmit(sk, skb, fl) : + quic_v6_lower_xmit(sk, skb, fl); + local_bh_enable(); +} + +#define quic_skb_ipv4(skb) (ip_hdr(skb)->version == 4) + +void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da, + union quic_addr *sa) +{ + memset(sa, 0, sizeof(*sa)); + memset(da, 0, sizeof(*da)); + quic_skb_ipv4(skb) ? quic_v4_get_msg_addrs(skb, da, sa) : + quic_v6_get_msg_addrs(skb, da, sa); +} + +int quic_get_mtu_info(struct sk_buff *skb, u32 *info) +{ + return quic_skb_ipv4(skb) ? quic_v4_get_mtu_info(skb, info) : + quic_v6_get_mtu_info(skb, info); +} + +#define quic_pf_ipv4(sk) ((sk)->sk_family == PF_INET) + +bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a, + union quic_addr *addr) +{ + return quic_pf_ipv4(sk) ? quic_v4_cmp_sk_addr(sk, a, addr) : + quic_v6_cmp_sk_addr(sk, a, addr); +} + +int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer) +{ + return quic_pf_ipv4(sock->sk) ? quic_v4_get_sk_addr(sock, a, peer) : + quic_v6_get_sk_addr(sock, a, peer); +} + +int quic_get_dev_if(struct sock *sk, union quic_addr *a) +{ + if (!quic_af_ipv4(a) && + ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL && + a->v6.sin6_scope_id) + return a->v6.sin6_scope_id; + + return sk->sk_bound_dev_if; +} + +void quic_set_skb_iif(struct sk_buff *skb) +{ + /* Save the inet/inet6 iif before skb dst/cb are cleared. */ + skb->skb_iif = quic_skb_ipv4(skb) ? inet_iif(skb) : inet6_iif(skb); +} + +int quic_common_setsockopt(struct sock *sk, int level, int optname, + sockptr_t optval, unsigned int optlen) +{ + return quic_pf_ipv4(sk) ? + ip_setsockopt(sk, level, optname, optval, optlen) : + ipv6_setsockopt(sk, level, optname, optval, optlen); +} + +int quic_common_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + return quic_pf_ipv4(sk) ? + ip_getsockopt(sk, level, optname, optval, optlen) : + ipv6_getsockopt(sk, level, optname, optval, optlen); +} + +bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb) +{ + return quic_skb_ipv4(skb) ? ip_sk_accept_pmtu(sk) : + ip6_sk_accept_pmtu(sk); +} + +void quic_sk_destruct(struct sock *sk) +{ + quic_pf_ipv4(sk) ? inet_sock_destruct(sk) : inet6_sock_destruct(sk); +} diff --git a/net/quic/family.h b/net/quic/family.h new file mode 100644 index 000000000000..02342c4b7c99 --- /dev/null +++ b/net/quic/family.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* QUIC kernel implementation + * (C) Copyright Red Hat Corp. 2023 + * + * This file is part of the QUIC kernel implementation + * + * Written or modified by: + * Xin Long + */ + +#define QUIC_PORT_LEN 2 +#define QUIC_ADDR4_LEN 4 +#define QUIC_ADDR6_LEN 16 + +#define QUIC_PREF_ADDR_LEN \ + (QUIC_ADDR4_LEN + QUIC_PORT_LEN + QUIC_ADDR6_LEN + QUIC_PORT_LEN) + +bool quic_is_any_addr(union quic_addr *a); +u32 quic_encap_len(union quic_addr *a); + +void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da, + struct flowi *fl); +int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa, + struct flowi *fl); +void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf, + union quic_addr *a); + +void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da, + union quic_addr *sa); +int quic_get_mtu_info(struct sk_buff *skb, u32 *info); + +bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a, + union quic_addr *addr); +int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer); + +int quic_get_dev_if(struct sock *sk, union quic_addr *a); +void quic_set_skb_iif(struct sk_buff *skb); + +int quic_common_setsockopt(struct sock *sk, int level, int optname, + sockptr_t optval, unsigned int optlen); +int quic_common_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb); +void quic_sk_destruct(struct sock *sk); diff --git a/net/quic/protocol.c b/net/quic/protocol.c index a53a2b1218a6..c5cd6d609d06 100644 --- a/net/quic/protocol.c +++ b/net/quic/protocol.c @@ -47,7 +47,7 @@ static int quic_inet_listen(struct socket *sock, int backlog) static int quic_inet_getname(struct socket *sock, struct sockaddr *uaddr, int peer) { - return -EOPNOTSUPP; + return quic_get_sk_addr(sock, uaddr, peer); } static __poll_t quic_inet_poll(struct file *file, struct socket *sock, diff --git a/net/quic/socket.c b/net/quic/socket.c index c2841caca5dc..1c97d95c61b2 100644 --- a/net/quic/socket.c +++ b/net/quic/socket.c @@ -121,7 +121,8 @@ static int quic_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) { if (level != SOL_QUIC) - return -EOPNOTSUPP; + return quic_common_setsockopt(sk, level, optname, optval, + optlen); return quic_do_setsockopt(sk, optname, optval, optlen); } @@ -136,7 +137,8 @@ static int quic_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { if (level != SOL_QUIC) - return -EOPNOTSUPP; + return quic_common_getsockopt(sk, level, optname, optval, + optlen); return quic_do_getsockopt(sk, optname, USER_SOCKPTR(optval), USER_SOCKPTR(optlen)); diff --git a/net/quic/socket.h b/net/quic/socket.h index 9a2f4b851676..0aa642e3b0ae 100644 --- a/net/quic/socket.h +++ b/net/quic/socket.h @@ -11,6 +11,7 @@ #include #include "common.h" +#include "family.h" #include "protocol.h" -- 2.47.1