net: avoid use IPCB in cipso_v4_error [Linux 5.0]

net: avoid use IPCB in cipso_v4_error [Linux 5.0]

This Linux kernel change "net: avoid use IPCB in cipso_v4_error" is included in the Linux 5.0 release. This change is authored by Nazarov Sergey <s-nazarov [at] yandex.ru> on Mon Feb 25 19:27:15 2019 +0300. The commit for this change in Linux stable tree is 3da1ed7 (patch).

net: avoid use IPCB in cipso_v4_error

Extract IP options in cipso_v4_error and use __icmp_send.

Signed-off-by: Sergey Nazarov <[email protected]>
Acked-by: Paul Moore <[email protected]>
Signed-off-by: David S. Miller <[email protected]>

There are 41 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 include/net/ip.h      |  2 ++
 net/ipv4/cipso_ipv4.c | 17 +++++++++++++++--
 net/ipv4/ip_options.c | 22 +++++++++++++++++-----
 3 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/include/net/ip.h b/include/net/ip.h
index 8866bfc..f0e8d06 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -667,6 +667,8 @@ static inline int ip_options_echo(struct net *net, struct ip_options *dopt,
 }

 void ip_options_fragment(struct sk_buff *skb);
+int __ip_options_compile(struct net *net, struct ip_options *opt,
+            struct sk_buff *skb, __be32 *info);
 int ip_options_compile(struct net *net, struct ip_options *opt,
               struct sk_buff *skb);
 int ip_options_get(struct net *net, struct ip_options_rcu **optp,
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 777fa3b..eff86a7 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -1735,13 +1735,26 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
  */
 void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
 {
+   unsigned char optbuf[sizeof(struct ip_options) + 40];
+   struct ip_options *opt = (struct ip_options *)optbuf;
+
    if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
        return;

+   /*
+    * We might be called above the IP layer,
+    * so we can not use icmp_send and IPCB here.
+    */
+
+   memset(opt, 0, sizeof(struct ip_options));
+   opt->optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
+   if (__ip_options_compile(dev_net(skb->dev), opt, skb, NULL))
+       return;
+
    if (gateway)
-       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
+       __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0, opt);
    else
-       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
+       __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0, opt);
 }

 /**
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index ed194d4..32a3504 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -251,8 +251,9 @@ static void spec_dst_fill(__be32 *spec_dst, struct sk_buff *skb)
  * If opt == NULL, then skb->data should point to IP header.
  */

-int ip_options_compile(struct net *net,
-              struct ip_options *opt, struct sk_buff *skb)
+int __ip_options_compile(struct net *net,
+            struct ip_options *opt, struct sk_buff *skb,
+            __be32 *info)
 {
    __be32 spec_dst = htonl(INADDR_ANY);
    unsigned char *pp_ptr = NULL;
@@ -468,11 +469,22 @@ int ip_options_compile(struct net *net,
        return 0;

 error:
-   if (skb) {
-       icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
-   }
+   if (info)
+       *info = htonl((pp_ptr-iph)<<24);
    return -EINVAL;
 }
+
+int ip_options_compile(struct net *net,
+              struct ip_options *opt, struct sk_buff *skb)
+{
+   int ret;
+   __be32 info;
+
+   ret = __ip_options_compile(net, opt, skb, &info);
+   if (ret != 0 && skb)
+       icmp_send(skb, ICMP_PARAMETERPROB, 0, info);
+   return ret;
+}
 EXPORT_SYMBOL(ip_options_compile);

 /*

Leave a Reply

Your email address will not be published. Required fields are marked *