--- zzzz-none-000/linux-4.9.279/net/ipv6/icmp.c 2021-08-08 06:38:54.000000000 +0000 +++ puma7-atom-6591-750/linux-4.9.279/net/ipv6/icmp.c 2023-02-08 11:43:43.000000000 +0000 @@ -69,6 +69,7 @@ #include #include #include +#include #include @@ -92,9 +93,10 @@ struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL)); else if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); if (!(type & ICMPV6_INFOMSG_MASK)) if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) @@ -384,6 +386,101 @@ return ERR_PTR(err); } +/* Send out an ICMP Destination Unreachable message on the same interface + * it was received on, bypassing routing lookups for the destination + */ +static void icmpv6_send_reply_onlink(struct sk_buff *skb, u8 type, u8 code) +{ + struct net *net = dev_net(skb->dev); + struct inet6_dev *idev; + struct sock *sk = NULL; + struct ipv6_pinfo *np = NULL; + struct in6_addr local_saddr; + struct in6_addr *saddr = &local_saddr; + const struct in6_addr *daddr = NULL; + struct icmp6hdr *icmph = icmp6_hdr(skb); + struct icmp6hdr tmp_hdr; + struct sockcm_cookie sockc_unused = {0}; + struct ipcm6_cookie ipc6; + struct flowi6 fl6; + struct icmpv6_msg msg; + struct dst_entry *dst = NULL; + int err = 0; + int len = 0; + + /* Set up temporary ICMPv6 header for this message */ + memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); + tmp_hdr.icmp6_type = type; + tmp_hdr.icmp6_code = code; + tmp_hdr.icmp6_cksum = 0; + + /* Get a socket */ + sk = icmpv6_xmit_lock(net); + if (sk == NULL){ + return; + } + np = inet6_sk(sk); + + /* New destination address is the source of the packet we are replying to */ + daddr = &(ipv6_hdr(skb)->saddr); + + /* Use the link-local address of the interface the packet was received on as the source */ + if (ipv6_get_lladdr(skb->dev, saddr, (IFA_F_DADFAILED|IFA_F_NODAD|IFA_F_DADFAILED))){ + return; + } + + /* Set up the route for the reply */ + icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); + fl6.fl6_icmp_code = code; + security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); + if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; + dst = icmp6_dst_alloc(skb->dev, &fl6); + if (IS_ERR(dst)) { + goto out; + } + + ipc6.tclass = np->tclass; + fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); + + ipc6.hlimit = np->hop_limit; + if (ipc6.hlimit < 0) + ipc6.hlimit = ip6_dst_hoplimit(dst); + ipc6.dontfrag = np->dontfrag; + ipc6.opt = NULL; + + idev = __in6_dev_get(skb->dev); + + msg.skb = skb; + msg.offset = skb_network_offset(skb); + msg.type = type; + + len = skb->len - msg.offset; + len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)); + if (len < 0) { + net_dbg_ratelimited("icmp: invalid length\n"); + goto out_dst_release; + } + + err = ip6_append_data(sk, icmpv6_getfrag, &msg, + len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), &ipc6, + &fl6, (struct rt6_info *)dst, + MSG_DONTWAIT, &sockc_unused); + + if (err) { + ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); + ip6_flush_pending_frames(sk); + } else { + err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, + len + sizeof(struct icmp6hdr)); + } +out_dst_release: + dst_release(dst); +out: + icmpv6_xmit_unlock(sk); +} + /* * Send an ICMP message in response to a packet in error */ @@ -473,6 +570,16 @@ return; } + /* Per RFC 7084 L-14, send ICMPv6 Destination Unreachable + * messages, code 5 (Source address failed ingress/egress policy) + * out on the same interface they were received on. Call a + * separate function to handle this special case and return. + */ + if (type == ICMPV6_DEST_UNREACH && code == ICMPV6_POLICY_FAIL) { + icmpv6_send_reply_onlink(skb, type, code); + return; + } + mip6_addr_swap(skb, parm); memset(&fl6, 0, sizeof(fl6)); @@ -486,6 +593,7 @@ fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); @@ -661,6 +769,7 @@ fl6.flowi6_oif = skb->dev->ifindex; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.flowi6_mark = mark; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); @@ -806,6 +915,8 @@ ICMP6MSGIN_INC_STATS(dev_net(dev), idev, type); + avm_pa_add_local_session(AVM_PA_NET_IP6_DEVINFO(dev_net(skb->dev)), skb, NULL); + switch (type) { case ICMPV6_ECHO_REQUEST: icmpv6_echo_reply(skb);