netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: zhuyj <zyjzyj2000@gmail.com>
To: David Lebrun <david.lebrun@uclouvain.be>
Cc: netdev <netdev@vger.kernel.org>
Subject: Re: [PATCH 4/9] ipv6: sr: add core files for SR HMAC support
Date: Wed, 19 Oct 2016 21:20:51 +0800	[thread overview]
Message-ID: <CAD=hENedNvTjBqcotySDuKym5VzTXghLYHr_xGEMu3D+5rcxHg@mail.gmail.com> (raw)
In-Reply-To: <1476715350-18983-5-git-send-email-david.lebrun@uclouvain.be>

 __attribute__((packed)) is potentially unsafe on some systems. The
symptom probably won't show up on an x86, which just makes the problem
more insidious; testing on x86 systems won't reveal the problem. (On
the x86, misaligned accesses are handled in hardware; if you
dereference an int* pointer that points to an odd address, it will be
a little slower than if it were properly aligned, but you'll get the
correct result.)

On some other systems, such as SPARC, attempting to access a
misaligned int object causes a bus error, crashing the program.

On Mon, Oct 17, 2016 at 10:42 PM, David Lebrun
<david.lebrun@uclouvain.be> wrote:
> This patch adds the necessary functions to compute and check the HMAC signature
> of an SR-enabled packet. Two HMAC algorithms are supported: hmac(sha1) and
> hmac(sha256).
>
> In order to avoid dynamic memory allocation for each HMAC computation,
> a per-cpu ring buffer is allocated for this purpose.
>
> Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
> ---
>  include/linux/seg6_hmac.h      |   6 +
>  include/net/seg6_hmac.h        |  61 ++++++
>  include/uapi/linux/seg6_hmac.h |  20 ++
>  net/ipv6/seg6_hmac.c           | 432 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 519 insertions(+)
>  create mode 100644 include/linux/seg6_hmac.h
>  create mode 100644 include/net/seg6_hmac.h
>  create mode 100644 include/uapi/linux/seg6_hmac.h
>  create mode 100644 net/ipv6/seg6_hmac.c
>
> diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h
> new file mode 100644
> index 0000000..da437eb
> --- /dev/null
> +++ b/include/linux/seg6_hmac.h
> @@ -0,0 +1,6 @@
> +#ifndef _LINUX_SEG6_HMAC_H
> +#define _LINUX_SEG6_HMAC_H
> +
> +#include <uapi/linux/seg6_hmac.h>
> +
> +#endif
> diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h
> new file mode 100644
> index 0000000..6e5ee6a
> --- /dev/null
> +++ b/include/net/seg6_hmac.h
> @@ -0,0 +1,61 @@
> +/*
> + *  SR-IPv6 implementation
> + *
> + *  Author:
> + *  David Lebrun <david.lebrun@uclouvain.be>
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *      modify it under the terms of the GNU General Public License
> + *      as published by the Free Software Foundation; either version
> + *      2 of the License, or (at your option) any later version.
> + */
> +
> +#ifndef _NET_SEG6_HMAC_H
> +#define _NET_SEG6_HMAC_H
> +
> +#include <net/flow.h>
> +#include <net/ip6_fib.h>
> +#include <net/sock.h>
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <linux/route.h>
> +#include <net/seg6.h>
> +#include <linux/seg6_hmac.h>
> +
> +#define SEG6_HMAC_MAX_DIGESTSIZE       160
> +#define SEG6_HMAC_RING_SIZE            256
> +
> +struct seg6_hmac_info {
> +       struct list_head list;
> +
> +       u32 hmackeyid;
> +       char secret[SEG6_HMAC_SECRET_LEN];
> +       u8 slen;
> +       u8 alg_id;
> +};
> +
> +struct seg6_hmac_algo {
> +       u8 alg_id;
> +       char name[64];
> +       struct crypto_shash * __percpu *tfms;
> +       struct shash_desc * __percpu *shashs;
> +};
> +
> +extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo,
> +                            struct ipv6_sr_hdr *hdr, struct in6_addr *saddr,
> +                            u8 *output);
> +extern struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key);
> +extern int seg6_hmac_info_add(struct net *net, u32 key,
> +                             struct seg6_hmac_info *hinfo);
> +extern int seg6_hmac_info_del(struct net *net, u32 key,
> +                             struct seg6_hmac_info *hinfo);
> +extern int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
> +                         struct ipv6_sr_hdr *srh);
> +extern bool seg6_hmac_validate_skb(struct sk_buff *skb);
> +extern int seg6_hmac_init(void);
> +extern void seg6_hmac_exit(void);
> +extern int seg6_hmac_net_init(struct net *net);
> +extern void seg6_hmac_net_exit(struct net *net);
> +
> +#endif
> diff --git a/include/uapi/linux/seg6_hmac.h b/include/uapi/linux/seg6_hmac.h
> new file mode 100644
> index 0000000..0b5eda7
> --- /dev/null
> +++ b/include/uapi/linux/seg6_hmac.h
> @@ -0,0 +1,20 @@
> +#ifndef _UAPI_LINUX_SEG6_HMAC_H
> +#define _UAPI_LINUX_SEG6_HMAC_H
> +
> +#define SEG6_HMAC_SECRET_LEN   64
> +#define SEG6_HMAC_FIELD_LEN    32
> +
> +struct sr6_tlv_hmac {
> +       __u8 type;
> +       __u8 len;
> +       __u16 reserved;
> +       __be32 hmackeyid;
> +       __u8 hmac[SEG6_HMAC_FIELD_LEN];
> +} __attribute__((packed));
> +
> +enum {
> +       SEG6_HMAC_ALGO_SHA1 = 1,
> +       SEG6_HMAC_ALGO_SHA256 = 2,
> +};
> +
> +#endif
> diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
> new file mode 100644
> index 0000000..91bd59a
> --- /dev/null
> +++ b/net/ipv6/seg6_hmac.c
> @@ -0,0 +1,432 @@
> +/*
> + *  SR-IPv6 implementation -- HMAC functions
> + *
> + *  Author:
> + *  David Lebrun <david.lebrun@uclouvain.be>
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *      modify it under the terms of the GNU General Public License
> + *      as published by the Free Software Foundation; either version
> + *      2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +#include <linux/sockios.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/in6.h>
> +#include <linux/icmpv6.h>
> +#include <linux/mroute6.h>
> +#include <linux/slab.h>
> +
> +#include <linux/netfilter.h>
> +#include <linux/netfilter_ipv6.h>
> +
> +#include <net/sock.h>
> +#include <net/snmp.h>
> +
> +#include <net/ipv6.h>
> +#include <net/protocol.h>
> +#include <net/transp_v6.h>
> +#include <net/rawv6.h>
> +#include <net/ndisc.h>
> +#include <net/ip6_route.h>
> +#include <net/addrconf.h>
> +#include <net/xfrm.h>
> +
> +#include <linux/cryptohash.h>
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> +#include <net/seg6.h>
> +#include <net/genetlink.h>
> +#include <net/seg6_hmac.h>
> +#include <linux/random.h>
> +
> +static char * __percpu *hmac_ring;
> +
> +static struct seg6_hmac_algo hmac_algos[] = {
> +       {
> +               .alg_id = SEG6_HMAC_ALGO_SHA1,
> +               .name = "hmac(sha1)",
> +       },
> +       {
> +               .alg_id = SEG6_HMAC_ALGO_SHA256,
> +               .name = "hmac(sha256)",
> +       },
> +};
> +
> +static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id)
> +{
> +       int i, alg_count;
> +       struct seg6_hmac_algo *algo;
> +
> +       alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
> +       for (i = 0; i < alg_count; i++) {
> +               algo = &hmac_algos[i];
> +               if (algo->alg_id == alg_id)
> +                       return algo;
> +       }
> +
> +       return NULL;
> +}
> +
> +static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize,
> +                    u8 *output, int outlen)
> +{
> +       struct crypto_shash *tfm;
> +       struct shash_desc *shash;
> +       struct seg6_hmac_algo *algo;
> +       int ret, dgsize;
> +
> +       algo = __hmac_get_algo(hinfo->alg_id);
> +       if (!algo)
> +               return -ENOENT;
> +
> +       tfm = *this_cpu_ptr(algo->tfms);
> +
> +       dgsize = crypto_shash_digestsize(tfm);
> +       if (dgsize > outlen) {
> +               pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n",
> +                        dgsize, outlen);
> +               return -ENOMEM;
> +       }
> +
> +       ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen);
> +       if (ret < 0) {
> +               pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret);
> +               goto failed;
> +       }
> +
> +       shash = *this_cpu_ptr(algo->shashs);
> +       shash->tfm = tfm;
> +
> +       ret = crypto_shash_digest(shash, text, psize, output);
> +       if (ret < 0) {
> +               pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret);
> +               goto failed;
> +       }
> +
> +       return dgsize;
> +
> +failed:
> +       return ret;
> +}
> +
> +int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
> +                     struct in6_addr *saddr, u8 *output)
> +{
> +       int plen, i, dgsize, wrsize;
> +       char *ring, *off;
> +       u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE];
> +       __be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid);
> +
> +       /* a 160-byte buffer for digest output allows to store highest known
> +        * hash function (RadioGatun) with up to 1216 bits
> +        */
> +
> +       /* saddr(16) + first_seg(1) + cleanup(1) + keyid(4) + seglist(16n) */
> +       plen = 16 + 1 + 1 + 4 + (hdr->first_segment + 1) * 16;
> +
> +       /* this limit allows for 14 segments */
> +       if (plen >= SEG6_HMAC_RING_SIZE)
> +               return -EMSGSIZE;
> +
> +       local_bh_disable();
> +       ring = *this_cpu_ptr(hmac_ring);
> +       off = ring;
> +       memcpy(off, saddr, 16);
> +       off += 16;
> +       *off++ = hdr->first_segment;
> +       *off++ = !!(sr_get_flags(hdr) & SR6_FLAG_CLEANUP) << 7;
> +       memcpy(off, &hmackeyid, 4);
> +       off += 4;
> +
> +       for (i = 0; i < hdr->first_segment + 1; i++) {
> +               memcpy(off, hdr->segments + i, 16);
> +               off += 16;
> +       }
> +
> +       dgsize = __do_hmac(hinfo, ring, plen, tmp_out,
> +                          SEG6_HMAC_MAX_DIGESTSIZE);
> +       local_bh_enable();
> +
> +       if (dgsize < 0)
> +               return dgsize;
> +
> +       wrsize = SEG6_HMAC_FIELD_LEN;
> +       if (wrsize > dgsize)
> +               wrsize = dgsize;
> +
> +       memset(output, 0, SEG6_HMAC_FIELD_LEN);
> +       memcpy(output, tmp_out, wrsize);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(seg6_hmac_compute);
> +
> +/* checks if an incoming SR-enabled packet's HMAC status matches
> + * the incoming policy.
> + *
> + * called with rcu_read_lock()
> + */
> +bool seg6_hmac_validate_skb(struct sk_buff *skb)
> +{
> +       struct inet6_dev *idev;
> +       struct ipv6_sr_hdr *srh;
> +       struct sr6_tlv_hmac *tlv;
> +       struct seg6_hmac_info *hinfo;
> +       struct net *net = dev_net(skb->dev);
> +       u8 hmac_output[SEG6_HMAC_FIELD_LEN];
> +
> +       idev = __in6_dev_get(skb->dev);
> +
> +       srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
> +
> +       tlv = seg6_get_tlv_hmac(srh);
> +
> +       /* mandatory check but no tlv */
> +       if (idev->cnf.seg6_require_hmac > 0 && !tlv)
> +               return false;
> +
> +       /* no check */
> +       if (idev->cnf.seg6_require_hmac < 0)
> +               return true;
> +
> +       /* check only if present */
> +       if (idev->cnf.seg6_require_hmac == 0 && !tlv)
> +               return true;
> +
> +       /* now, seg6_require_hmac >= 0 && tlv */
> +
> +       hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
> +       if (!hinfo)
> +               return false;
> +
> +       if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output))
> +               return false;
> +
> +       if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +/* called with rcu_read_lock() */
> +struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key)
> +{
> +       struct seg6_pernet_data *sdata = seg6_pernet(net);
> +       struct seg6_hmac_info *hinfo;
> +
> +       list_for_each_entry_rcu(hinfo, &sdata->hmac_infos, list) {
> +               if (hinfo->hmackeyid == key)
> +                       return hinfo;
> +       }
> +
> +       return NULL;
> +}
> +EXPORT_SYMBOL(seg6_hmac_info_lookup);
> +
> +int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
> +{
> +       struct seg6_pernet_data *sdata = seg6_pernet(net);
> +       struct seg6_hmac_info *old_hinfo;
> +
> +       old_hinfo = seg6_hmac_info_lookup(net, key);
> +       if (old_hinfo)
> +               return -EEXIST;
> +
> +       list_add_rcu(&hinfo->list, &sdata->hmac_infos);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(seg6_hmac_info_add);
> +
> +int seg6_hmac_info_del(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
> +{
> +       struct seg6_hmac_info *tmp;
> +
> +       tmp = seg6_hmac_info_lookup(net, key);
> +       if (!tmp)
> +               return -ENOENT;
> +
> +       /* entry was replaced, ignore deletion */
> +       if (tmp != hinfo)
> +               return -ENOENT;
> +
> +       list_del_rcu(&hinfo->list);
> +       synchronize_net();
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(seg6_hmac_info_del);
> +
> +static void seg6_hmac_info_flush(struct net *net)
> +{
> +       struct seg6_pernet_data *sdata = seg6_pernet(net);
> +       struct seg6_hmac_info *hinfo;
> +
> +       seg6_pernet_lock(net);
> +       while ((hinfo = list_first_or_null_rcu(&sdata->hmac_infos,
> +                                              struct seg6_hmac_info,
> +                                              list)) != NULL) {
> +               list_del_rcu(&hinfo->list);
> +               seg6_pernet_unlock(net);
> +               synchronize_net();
> +               kfree(hinfo);
> +               seg6_pernet_lock(net);
> +       }
> +
> +       seg6_pernet_unlock(net);
> +}
> +
> +int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
> +                  struct ipv6_sr_hdr *srh)
> +{
> +       struct seg6_hmac_info *hinfo;
> +       int err = -ENOENT;
> +       struct sr6_tlv_hmac *tlv;
> +
> +       tlv = seg6_get_tlv(srh, SR6_TLV_HMAC);
> +       if (!tlv)
> +               return -EINVAL;
> +
> +       rcu_read_lock();
> +
> +       hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
> +       if (!hinfo)
> +               goto out;
> +
> +       memset(tlv->hmac, 0, SEG6_HMAC_FIELD_LEN);
> +       err = seg6_hmac_compute(hinfo, srh, saddr, tlv->hmac);
> +
> +out:
> +       rcu_read_unlock();
> +       return err;
> +}
> +EXPORT_SYMBOL(seg6_push_hmac);
> +
> +static int seg6_hmac_init_ring(void)
> +{
> +       int i;
> +
> +       hmac_ring = alloc_percpu(char *);
> +
> +       if (!hmac_ring)
> +               return -ENOMEM;
> +
> +       for_each_possible_cpu(i) {
> +               char *ring = kzalloc(SEG6_HMAC_RING_SIZE, GFP_KERNEL);
> +
> +               if (!ring)
> +                       return -ENOMEM;
> +
> +               *per_cpu_ptr(hmac_ring, i) = ring;
> +       }
> +
> +       return 0;
> +}
> +
> +static int seg6_hmac_init_algo(void)
> +{
> +       int i, alg_count, cpu;
> +       struct seg6_hmac_algo *algo;
> +       struct crypto_shash *tfm;
> +       struct shash_desc *shash;
> +
> +       alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
> +
> +       for (i = 0; i < alg_count; i++) {
> +               int shsize;
> +               struct crypto_shash **p_tfm;
> +
> +               algo = &hmac_algos[i];
> +               algo->tfms = alloc_percpu(struct crypto_shash *);
> +               if (!algo->tfms)
> +                       return -ENOMEM;
> +
> +               for_each_possible_cpu(cpu) {
> +                       tfm = crypto_alloc_shash(algo->name, 0, GFP_KERNEL);
> +                       if (IS_ERR(tfm))
> +                               return PTR_ERR(tfm);
> +                       p_tfm = per_cpu_ptr(algo->tfms, cpu);
> +                       *p_tfm = tfm;
> +               }
> +
> +               p_tfm = this_cpu_ptr(algo->tfms);
> +               tfm = *p_tfm;
> +
> +               shsize = sizeof(*shash) + crypto_shash_descsize(tfm);
> +
> +               algo->shashs = alloc_percpu(struct shash_desc *);
> +               if (!algo->shashs)
> +                       return -ENOMEM;
> +
> +               for_each_possible_cpu(cpu) {
> +                       shash = kzalloc(shsize, GFP_KERNEL);
> +                       if (!shash)
> +                               return -ENOMEM;
> +                       *per_cpu_ptr(algo->shashs, cpu) = shash;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +int __init seg6_hmac_init(void)
> +{
> +       int ret;
> +
> +       ret = seg6_hmac_init_ring();
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = seg6_hmac_init_algo();
> +
> +out:
> +       return ret;
> +}
> +
> +int __net_init seg6_hmac_net_init(struct net *net)
> +{
> +       struct seg6_pernet_data *sdata = seg6_pernet(net);
> +
> +       INIT_LIST_HEAD(&sdata->hmac_infos);
> +       return 0;
> +}
> +
> +void __exit seg6_hmac_exit(void)
> +{
> +       int i, alg_count, cpu;
> +       struct seg6_hmac_algo *algo = NULL;
> +
> +       for_each_possible_cpu(i) {
> +               char *ring = *per_cpu_ptr(hmac_ring, i);
> +
> +               kfree(ring);
> +       }
> +       free_percpu(hmac_ring);
> +
> +       alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
> +       for (i = 0; i < alg_count; i++) {
> +               algo = &hmac_algos[i];
> +               for_each_possible_cpu(cpu) {
> +                       struct crypto_shash *tfm;
> +                       struct shash_desc *shash;
> +
> +                       shash = *per_cpu_ptr(algo->shashs, cpu);
> +                       kfree(shash);
> +                       tfm = *per_cpu_ptr(algo->tfms, cpu);
> +                       crypto_free_shash(tfm);
> +               }
> +               free_percpu(algo->tfms);
> +               free_percpu(algo->shashs);
> +       }
> +}
> +
> +void __net_exit seg6_hmac_net_exit(struct net *net)
> +{
> +       seg6_hmac_info_flush(net);
> +}
> --
> 2.7.3
>

  parent reply	other threads:[~2016-10-19 14:26 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-17 14:42 [PATCH 0/9] net: add support for IPv6 Segment Routing David Lebrun
2016-10-17 14:42 ` [PATCH 1/9] ipv6: implement dataplane support for rthdr type 4 (Segment Routing Header) David Lebrun
2016-10-17 14:57   ` David Miller
2016-10-17 15:04     ` David Lebrun
2016-10-17 17:01   ` Tom Herbert
2016-10-18 11:43     ` David Lebrun
2016-10-20 13:04     ` David Lebrun
2016-10-20 15:47       ` Tom Herbert
2016-10-17 17:31   ` Tom Herbert
2016-10-17 14:42 ` [PATCH 2/9] ipv6: sr: add code base for control plane support of SR-IPv6 David Lebrun
2016-10-17 15:00   ` David Miller
2016-10-17 15:06     ` David Lebrun
2016-10-17 15:21       ` David Miller
2016-10-17 17:07   ` Tom Herbert
2016-10-18 11:45     ` David Lebrun
2016-10-17 14:42 ` [PATCH 3/9] ipv6: sr: add support for SRH encapsulation and injection with lwtunnels David Lebrun
2016-10-17 17:17   ` Tom Herbert
2016-10-18 11:47     ` David Lebrun
2016-10-17 14:42 ` [PATCH 4/9] ipv6: sr: add core files for SR HMAC support David Lebrun
2016-10-17 17:24   ` Tom Herbert
2016-10-18 12:07     ` David Lebrun
2016-10-19 13:20   ` zhuyj [this message]
2016-10-19 14:43     ` David Laight
2016-10-17 14:43 ` [PATCH 5/9] ipv6: sr: implement API to control SR HMAC structures David Lebrun
2016-10-17 14:43 ` [PATCH 6/9] ipv6: sr: add calls to verify and insert HMAC signatures David Lebrun
2016-10-17 14:43 ` [PATCH 7/9] ipv6: add source address argument for ipv6_push_nfrag_opts David Lebrun
2016-10-17 14:43 ` [PATCH 8/9] ipv6: sr: add support for SRH injection through setsockopt David Lebrun
2016-10-17 14:43 ` [PATCH 9/9] ipv6: sr: add documentation file for per-interface sysctls David Lebrun
2016-10-17 15:09 ` [PATCH 0/9] net: add support for IPv6 Segment Routing David Laight

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAD=hENedNvTjBqcotySDuKym5VzTXghLYHr_xGEMu3D+5rcxHg@mail.gmail.com' \
    --to=zyjzyj2000@gmail.com \
    --cc=david.lebrun@uclouvain.be \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).