From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Ansis Atteka <aatteka@nicira.com>
Cc: netfilter-devel@vger.kernel.org
Subject: Re: [PATCHv2] conntrackd: make conntrackd namespace aware
Date: Tue, 18 Sep 2012 20:08:02 +0200 [thread overview]
Message-ID: <20120918180802.GA28256@1984> (raw)
In-Reply-To: <1347331857-6577-1-git-send-email-aatteka@nicira.com>
On Mon, Sep 10, 2012 at 07:50:57PM -0700, Ansis Atteka wrote:
> This patch allows conntrackd to open CT Netlink sockets into a given
> network namespace. Channel sockets (e.g. UDP) would still be opened into
> the same namespace where conntrackd was started.
>
> The only binary this patch affects is conntrackd. All other binaries (e.g.
> conntrack, nfct) would still operate in the same namespace where they were
> started.
>
> To make use of this patch:
> 1. create a network namespace: "ip netns add the_ns"
> 2. add "NetlinkNamespace /var/run/netns/the_ns" line to the conntrackd.conf
> file inside General {...} section.
I have to request more comestical changes, and minor fixes.
> Signed-off-by: Ansis Atteka <aatteka@nicira.com>
> ---
> configure.ac | 2 +-
> include/Makefile.am | 2 +-
> include/conntrackd.h | 6 +++
> include/namespace.h | 13 ++++++
> src/Makefile.am | 3 +-
> src/cthelper.c | 4 +-
> src/ctnl.c | 17 +++++--
> src/expect.c | 3 +-
> src/external_inject.c | 3 +-
> src/internal_bypass.c | 5 +-
> src/namespace.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/netlink.c | 6 ++-
> src/read_config_lex.l | 1 +
> src/read_config_yy.y | 8 +++-
> src/run.c | 1 +
> src/sync-mode.c | 4 +-
> src/sync-notrack.c | 3 +-
> 17 files changed, 186 insertions(+), 17 deletions(-)
> create mode 100644 include/namespace.h
> create mode 100644 src/namespace.c
>
> diff --git a/configure.ac b/configure.ac
> index 27ad01b..98a8091 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -70,7 +70,7 @@ dnl AC_CHECK_LIB([c], [main])
>
> AC_CHECK_HEADERS(arpa/inet.h)
> dnl check for inet_pton
> -AC_CHECK_FUNCS(inet_pton)
> +AC_CHECK_FUNCS([inet_pton, setns])
> dnl Some systems have it, but not IPv6
> if test "$ac_cv_func_inet_pton" = "yes" ; then
> AC_MSG_CHECKING(if inet_pton supports IPv6)
> diff --git a/include/Makefile.am b/include/Makefile.am
> index 6bd0f7f..e06fd4d 100644
> --- a/include/Makefile.am
> +++ b/include/Makefile.am
> @@ -6,5 +6,5 @@ noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \
> network.h filter.h queue.h vector.h cidr.h \
> traffic_stats.h netlink.h fds.h event.h bitops.h channel.h \
> process.h origin.h internal.h external.h date.h nfct.h \
> - helper.h myct.h stack.h
> + helper.h myct.h stack.h namespace.h
>
> diff --git a/include/conntrackd.h b/include/conntrackd.h
> index 19e613c..a069793 100644
> --- a/include/conntrackd.h
> +++ b/include/conntrackd.h
> @@ -94,6 +94,7 @@ struct ct_conf {
> int channel_type_global;
> struct channel_conf channel[MULTICHANNEL_MAX];
> struct local_conf local; /* unix socket facilities */
> + char netlink_namespace[FILENAME_MAXLEN];
> int nice;
> int limit;
> int refresh;
> @@ -151,6 +152,11 @@ struct ct_general_state {
> struct ct_filter *us_filter;
> struct exp_filter *exp_filter;
>
> + /* namespace file descriptors for conntrackd sockets */
> + struct {
> + int cur_fd; /* conntrack */
> + int root_fd; /* channel*/
> + } ns;
> struct nfct_handle *event; /* event handler */
> struct nfct_filter *filter; /* event filter */
> int event_iterations_limit;
> diff --git a/include/namespace.h b/include/namespace.h
> new file mode 100644
> index 0000000..e6dcf0a
> --- /dev/null
> +++ b/include/namespace.h
> @@ -0,0 +1,13 @@
> +#ifndef _NAMESPACE_H_
> +#define _NAMESPACE_H_
> +
> +#include <libmnl/libmnl.h>
> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
> +
> +#include "conntrackd.h"
> +
> +int ns_init(char[FILENAME_MAXLEN]);
> +struct nfct_handle *ns_nfct_open(u_int8_t, unsigned, int);
> +struct mnl_socket *ns_mnl_socket_open(int, int);
> +
> +#endif
> diff --git a/src/Makefile.am b/src/Makefile.am
> index d8074d2..60314ab 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -39,7 +39,8 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
> external_cache.c external_inject.c \
> internal_cache.c internal_bypass.c \
> read_config_yy.y read_config_lex.l \
> - stack.c helpers.c utils.c expect.c
> + stack.c helpers.c utils.c expect.c \
> + namespace.c
>
> # yacc and lex generate dirty code
> read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
> diff --git a/src/cthelper.c b/src/cthelper.c
> index c119869..ddb7fe6 100644
> --- a/src/cthelper.c
> +++ b/src/cthelper.c
> @@ -22,6 +22,7 @@
> #include "log.h"
> #include "fds.h"
> #include "helper.h"
> +#include "namespace.h"
>
> #include <unistd.h>
> #include <fcntl.h>
> @@ -493,7 +494,8 @@ int cthelper_init(void)
> return -1;
> }
>
> - STATE_CTH(nl) = mnl_socket_open(NETLINK_NETFILTER);
> + STATE_CTH(nl) = ns_mnl_socket_open(NETLINK_NETFILTER,
> + STATE(ns).cur_fd);
Please do it like:
STATE_CTH(nl) = ns_mnl_socket_open(NETLINK_NETFILTER,
STATE(ns).cur_fd);
It's the same like Linux kernel codying style.
Same thing for the entire patch.
> if (STATE_CTH(nl) == NULL) {
> dlog(LOG_ERR, "cannot open nfq socket");
> return -1;
> diff --git a/src/ctnl.c b/src/ctnl.c
> index bb54727..b8c9a85 100644
> --- a/src/ctnl.c
> +++ b/src/ctnl.c
> @@ -29,6 +29,7 @@
> #include "origin.h"
> #include "date.h"
> #include "internal.h"
> +#include "namespace.h"
>
> #include <errno.h>
> #include <signal.h>
> @@ -399,6 +400,10 @@ static void poll_cb(void *data)
>
> int ctnl_init(void)
> {
> + if (ns_init(CONFIG(netlink_namespace)) == -1) {
> + return -1;
> + }
No need for brackets if the `if' statement contains just one line.
> +
> if (CONFIG(flags) & CTD_STATS_MODE)
> STATE(mode) = &stats_mode;
> else if (CONFIG(flags) & CTD_SYNC_MODE)
> @@ -417,7 +422,8 @@ int ctnl_init(void)
> }
>
> /* resynchronize (like 'dump' socket) but it also purges old entries */
> - STATE(resync) = nfct_open(CONFIG(netlink).subsys_id, 0);
> + STATE(resync) = ns_nfct_open(CONFIG(netlink).subsys_id, 0,
> + STATE(ns).cur_fd);
> if (STATE(resync)== NULL) {
> dlog(LOG_ERR, "can't open netlink handler: %s",
> strerror(errno));
> @@ -438,7 +444,8 @@ int ctnl_init(void)
> fcntl(nfct_fd(STATE(resync)), F_SETFL, O_NONBLOCK);
>
> if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) {
> - STATE(dump) = nfct_open(CONFIG(netlink).subsys_id, 0);
> + STATE(dump) = ns_nfct_open(CONFIG(netlink).subsys_id, 0,
> + STATE(ns).cur_fd);
> if (STATE(dump) == NULL) {
> dlog(LOG_ERR, "can't open netlink handler: %s",
> strerror(errno));
> @@ -467,7 +474,8 @@ int ctnl_init(void)
> }
> }
>
> - STATE(get) = nfct_open(CONFIG(netlink).subsys_id, 0);
> + STATE(get) = ns_nfct_open(CONFIG(netlink).subsys_id, 0,
> + STATE(ns).cur_fd);
> if (STATE(get) == NULL) {
> dlog(LOG_ERR, "can't open netlink handler: %s",
> strerror(errno));
> @@ -481,7 +489,8 @@ int ctnl_init(void)
> exp_get_handler, NULL);
> }
>
> - STATE(flush) = nfct_open(CONFIG(netlink).subsys_id, 0);
> + STATE(flush) = ns_nfct_open(CONFIG(netlink).subsys_id, 0,
> + STATE(ns).cur_fd);
> if (STATE(flush) == NULL) {
> dlog(LOG_ERR, "cannot open flusher handler");
> return -1;
> diff --git a/src/expect.c b/src/expect.c
> index 6069770..c8bdd16 100644
> --- a/src/expect.c
> +++ b/src/expect.c
> @@ -9,6 +9,7 @@
> */
>
> #include "helper.h"
> +#include "namespace.h"
>
> #include <stdio.h>
> #include <string.h>
> @@ -165,7 +166,7 @@ static int cthelper_expect_cmd(struct nf_expect *exp, int cmd)
> int ret;
> struct nfct_handle *h;
>
> - h = nfct_open(EXPECT, 0);
> + h = ns_nfct_open(EXPECT, 0, STATE(ns).cur_fd);
> if (!h)
> return -1;
>
> diff --git a/src/external_inject.c b/src/external_inject.c
> index 0ad3478..1a65fa3 100644
> --- a/src/external_inject.c
> +++ b/src/external_inject.c
> @@ -22,6 +22,7 @@
> #include "cache.h"
> #include "origin.h"
> #include "external.h"
> +#include "namespace.h"
> #include "netlink.h"
>
> #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
> @@ -42,7 +43,7 @@ struct {
> static int external_inject_init(void)
> {
> /* handler to directly inject conntracks into kernel-space */
> - inject = nfct_open(CONFIG(netlink).subsys_id, 0);
> + inject = ns_nfct_open(CONFIG(netlink).subsys_id, 0, STATE(ns).cur_fd);
> if (inject == NULL) {
> dlog(LOG_ERR, "can't open netlink handler: %s",
> strerror(errno));
> diff --git a/src/internal_bypass.c b/src/internal_bypass.c
> index 1194339..d0671e6 100644
> --- a/src/internal_bypass.c
> +++ b/src/internal_bypass.c
> @@ -16,6 +16,7 @@
> #include "netlink.h"
> #include "network.h"
> #include "origin.h"
> +#include "namespace.h"
>
> static int internal_bypass_init(void)
> {
> @@ -52,7 +53,7 @@ static void internal_bypass_ct_dump(int fd, int type)
> u_int32_t family = AF_UNSPEC;
> int ret;
>
> - h = nfct_open(CONFIG(netlink).subsys_id, 0);
> + h = ns_nfct_open(CONFIG(netlink).subsys_id, 0,STATE(ns).cur_fd);
> if (h == NULL) {
> dlog(LOG_ERR, "can't allocate memory for the internal cache");
> return;
> @@ -183,7 +184,7 @@ static void internal_bypass_exp_dump(int fd, int type)
> u_int32_t family = AF_UNSPEC;
> int ret;
>
> - h = nfct_open(CONFIG(netlink).subsys_id, 0);
> + h = ns_nfct_open(CONFIG(netlink).subsys_id, 0, STATE(ns).cur_fd);
> if (h == NULL) {
> dlog(LOG_ERR, "can't allocate memory for the internal cache");
> return;
> diff --git a/src/namespace.c b/src/namespace.c
> new file mode 100644
> index 0000000..3f64cb5
> --- /dev/null
> +++ b/src/namespace.c
> @@ -0,0 +1,122 @@
> +/*
> + * Copyright (C) 2012 Nicira, Inc.
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Authors:
> + * Ansis Atteka <aatteka@nicira.com>
> + */
> +#define _GNU_SOURCE
> +
> +#include "namespace.h"
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sched.h>
> +#include <sys/syscall.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +
> +#include "log.h"
> +
> +#ifndef HAVE_SETNS
> +static int setns(int fd, int nstype) {
> +#ifdef __NR_setns
> +#define HAVE_SETNS
> + return syscall(__NR_setns, fd, nstype);
> +#else
> + return 0;
> +#endif
> +}
> +#endif /* HAVE_SETNS */
> +
> +static int
> +ctd_change_namespace(int fd)
> +{
> + if (fd != -1) {
> +#ifdef HAVE_SETNS
> + if (setns(fd, CLONE_NEWNET)) {
> + dlog(LOG_ERR, "could not switch to namespace: %s",
> + strerror(errno));
> + return -1;
> + }
> +#else
> + dlog(LOG_ERR, "conntrackd was not built with namespace "
> + "support");
ns_init already spots a warning if HAVE_SETNS is not set. I think we
don't need this. I'd suggest this so you save one level of indent:
static int
ctd_change_namespace(int fd)
{
if (fd == -1)
return 0;
#ifdef HAVE_SETNS
if (setns(fd, CLONE_NEWNET)) {
dlog(LOG_ERR, "could not switch to namespace: %s",
strerror(errno));
return -1;
}
#endif
return 0;
}
> +#endif
> + }
> + return 0;
> +}
> +
> +int ns_init(char netlink_namespace[FILENAME_MAXLEN]) {
You pass netlink_namespace[...] but the you use CONFIG(...), so this
parameter is useless.
I don't mind if you want to pass CONFIG, but then use char * as
parameter, please.
> + STATE(ns).root_fd = -1;
> + STATE(ns).cur_fd = -1;
> + if (CONFIG(netlink_namespace)[0]) {
> +#ifndef HAVE_SETNS
> + dlog(LOG_ERR, "conntrackd was not built with namespace "
> + "support");
> + return -1;
> +#else
> + STATE(ns).cur_fd = open(CONFIG(netlink_namespace), O_RDONLY);
> + if (STATE(ns).cur_fd == -1) {
> + dlog(LOG_ERR, "couldn't open namespace %s: %s",
> + netlink_namespace,
> + strerror(errno));
> + return -1;
> + }
> + STATE(ns).root_fd = open("/proc/self/ns/net", O_RDONLY);
> + if (STATE(ns).root_fd == -1) {
> + dlog(LOG_WARNING, "couldn't open root namespace: %s",
> + strerror(errno));
> + return -1;
> + }
> +#endif
> + }
> + return 0;
> +}
I suggest:
static int do_ns_init(void)
{
STATE(ns).cur_fd = open(CONFIG(netlink_namespace), O_RDONLY);
if (STATE(ns).cur_fd == -1) {
dlog(LOG_ERR, "couldn't open namespace %s: %s",
netlink_namespace,
strerror(errno));
return -1;
}
STATE(ns).root_fd = open("/proc/self/ns/net", O_RDONLY);
if (STATE(ns).root_fd == -1) {
dlog(LOG_WARNING, "couldn't open root namespace: %s",
strerror(errno));
return -1;
}
return 0;
}
int ns_init(void)
{
int ret = 0;
STATE(ns).root_fd = -1;
STATE(ns).cur_fd = -1;
if (!CONFIG(netlink_namespace)[0])
return 0;
#ifndef HAVE_SETNS
dlog(LOG_ERR, "conntrackd was not built with namespace support");
return -1;
#else
ret = do_ns_init();
#endif
return ret;
}
> +
> +struct nfct_handle *ns_nfct_open(u_int8_t subsys_id, unsigned subscriptions,
> + int ns_fd) {
brackets for functions always start in the new line, ie.
func(...)
{
...
}
> + struct nfct_handle *handle;
This need handle = NULL. See below why.
> +
> + if (ctd_change_namespace(ns_fd)) {
for error, better check < 0. Convention is that functions return
negative value for errors.
> + return NULL;
> + }
no need for brackets here.
> + handle = nfct_open(subsys_id, subscriptions);
> + if (ctd_change_namespace(STATE(ns).root_fd)) {
> + if (handle) {
If you do this, you have to initialize handle to NULL.
> + nfct_close(handle);
> + }
no need for brackets, if statement with just one line.
> + return NULL;
> + }
> + return handle;
> +}
> +
> +struct mnl_socket *ns_mnl_socket_open(int bus, int ns_fd) {
> + struct mnl_socket *handle;
> +
> + if (ctd_change_namespace(ns_fd)) {
> + return NULL;
> + }
> + handle = mnl_socket_open(bus);
> + if (ctd_change_namespace(STATE(ns).root_fd)) {
> + if (handle) {
> + mnl_socket_close(handle);
> + }
> + return NULL;
> + }
> + return handle;
> +}
same comments as above.
> diff --git a/src/netlink.c b/src/netlink.c
> index bd38d99..ff8967c 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -21,6 +21,7 @@
> #include "conntrackd.h"
> #include "filter.h"
> #include "log.h"
> +#include "namespace.h"
>
> #include <string.h>
> #include <errno.h>
> @@ -33,7 +34,8 @@ struct nfct_handle *nl_init_event_handler(void)
> {
> struct nfct_handle *h;
>
> - h = nfct_open(CONFIG(netlink).subsys_id, CONFIG(netlink).groups);
> + h = ns_nfct_open(CONFIG(netlink).subsys_id, CONFIG(netlink).groups,
> + STATE(ns).cur_fd);
> if (h == NULL)
> return NULL;
>
> @@ -175,7 +177,7 @@ int nl_flush_conntrack_table_selective(void)
> struct nfct_handle *h;
> int ret;
>
> - h = nfct_open(CONNTRACK, 0);
> + h = ns_nfct_open(CONNTRACK, 0, STATE(ns).cur_fd);
> if (h == NULL) {
> dlog(LOG_ERR, "cannot open handle");
> return -1;
> diff --git a/src/read_config_lex.l b/src/read_config_lex.l
> index 31fa32e..7a09c52 100644
> --- a/src/read_config_lex.l
> +++ b/src/read_config_lex.l
> @@ -91,6 +91,7 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k]
> "SocketBufferSizeMaxGrowth" { return T_BUFFER_SIZE_MAX_GROWN; /* alias */ }
> "NetlinkBufferSize" { return T_BUFFER_SIZE; }
> "NetlinkBufferSizeMaxGrowth" { return T_BUFFER_SIZE_MAX_GROWN; }
> +"NetlinkNamespace" { return T_NETLINK_NAMESPACE; }
> "Mode" { return T_SYNC_MODE; }
> "ListenTo" { return T_LISTEN_TO; }
> "Family" { return T_FAMILY; }
> diff --git a/src/read_config_yy.y b/src/read_config_yy.y
> index c9235d3..b0985e7 100644
> --- a/src/read_config_yy.y
> +++ b/src/read_config_yy.y
> @@ -87,7 +87,7 @@ enum {
> %token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH
> %token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC
> %token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_POLICY T_HELPER_EXPECT_MAX
> -%token T_HELPER_EXPECT_TIMEOUT
> +%token T_HELPER_EXPECT_TIMEOUT T_NETLINK_NAMESPACE
>
> %token <string> T_IP T_PATH_VAL
> %token <val> T_NUMBER
> @@ -1113,6 +1113,7 @@ general_line: hashsize
> | unix_line
> | netlink_buffer_size
> | netlink_buffer_size_max_grown
> + | netlink_namespace
> | family
> | event_iterations_limit
> | poll_secs
> @@ -1158,6 +1159,11 @@ netlink_events_reliable : T_NETLINK_EVENTS_RELIABLE T_OFF
> conf.netlink.events_reliable = 0;
> };
>
> +netlink_namespace : T_NETLINK_NAMESPACE T_PATH_VAL
> +{
> + strncpy(conf.netlink_namespace, $2, FILENAME_MAXLEN);
> +};
> +
> nice : T_NICE T_SIGNED_NUMBER
> {
> conf.nice = $2;
> diff --git a/src/run.c b/src/run.c
> index 3337694..ea7ce33 100644
> --- a/src/run.c
> +++ b/src/run.c
> @@ -30,6 +30,7 @@
> #include "origin.h"
> #include "date.h"
> #include "internal.h"
> +#include "namespace.h"
>
> #include <errno.h>
> #include <signal.h>
> diff --git a/src/sync-mode.c b/src/sync-mode.c
> index e69ecfe..8a9abad 100644
> --- a/src/sync-mode.c
> +++ b/src/sync-mode.c
> @@ -31,6 +31,7 @@
> #include "origin.h"
> #include "internal.h"
> #include "external.h"
> +#include "namespace.h"
>
> #include <errno.h>
> #include <unistd.h>
> @@ -451,7 +452,8 @@ static int init_sync(void)
> tx_queue_cb, NULL, STATE(fds)) == -1)
> return -1;
>
> - STATE_SYNC(commit).h = nfct_open(CONFIG(netlink).subsys_id, 0);
> + STATE_SYNC(commit).h = ns_nfct_open(CONFIG(netlink).subsys_id, 0,
> + STATE(ns).cur_fd);
> if (STATE_SYNC(commit).h == NULL) {
> dlog(LOG_ERR, "can't create handler to commit");
> return -1;
> diff --git a/src/sync-notrack.c b/src/sync-notrack.c
> index a7df4e7..1cc0aac 100644
> --- a/src/sync-notrack.c
> +++ b/src/sync-notrack.c
> @@ -24,6 +24,7 @@
> #include "log.h"
> #include "cache.h"
> #include "fds.h"
> +#include "namespace.h"
>
> #include <string.h>
>
> @@ -102,7 +103,7 @@ static void kernel_resync(void)
> u_int32_t family = AF_UNSPEC;
> int ret;
>
> - h = nfct_open(CONFIG(netlink).subsys_id, 0);
> + h = ns_nfct_open(CONFIG(netlink).subsys_id, 0, STATE(ns).cur_fd);
> if (h == NULL) {
> dlog(LOG_ERR, "can't allocate memory for the internal cache");
> return;
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2012-09-18 18:08 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-09-11 2:50 [PATCHv2] conntrackd: make conntrackd namespace aware Ansis Atteka
2012-09-18 18:08 ` Pablo Neira Ayuso [this message]
2012-09-18 18:53 ` Pablo Neira Ayuso
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=20120918180802.GA28256@1984 \
--to=pablo@netfilter.org \
--cc=aatteka@nicira.com \
--cc=netfilter-devel@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).