* [PATCH] Introduce a sysctl that modifies the value of PROT_SOCK.
@ 2016-12-31 4:11 Krister Johansen
2016-12-31 20:55 ` Stephen Hemminger
0 siblings, 1 reply; 11+ messages in thread
From: Krister Johansen @ 2016-12-31 4:11 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
Add net.ipv4.ip_unprotected_port_start, which is a per namespace sysctl
that denotes the first unprotected inet port in the namespace. To
disable all protected ports set this to zero. It also checks for
overlap with the local port range. The protected and local range may
not overlap.
The use case for this change is to allow containerized processes to bind
to priviliged ports, but prevent them from ever being allowed to modify
their container's network configuration. The latter is accomplished by
ensuring that the network namespace is not a child of the user
namespace. This modification was needed to allow the container manager
to disable a namespace's priviliged port restrictions without exposing
control of the network namespace to processes in the user namespace.
Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
include/net/ip.h | 12 +++++++++
include/net/netns/ipv4.h | 3 +++
net/Kconfig | 7 +++++
net/ipv4/af_inet.c | 5 +++-
net/ipv4/sysctl_net_ipv4.c | 59 ++++++++++++++++++++++++++++++++++++++++++
net/ipv6/af_inet6.c | 3 ++-
net/netfilter/ipvs/ip_vs_ctl.c | 7 +++--
net/sctp/socket.c | 10 ++++---
security/selinux/hooks.c | 3 ++-
9 files changed, 98 insertions(+), 11 deletions(-)
diff --git a/include/net/ip.h b/include/net/ip.h
index ab6761a..65f1375 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -270,6 +270,18 @@ static inline int inet_is_local_reserved_port(struct net *net, int port)
}
#endif
+#ifdef CONFIG_LOWPORT_SYSCTL
+static inline int inet_prot_sock(struct net *net)
+{
+ return net->ipv4.sysctl_ip_prot_sock;
+}
+#else
+static inline int inet_prot_sock(struct net *net)
+{
+ return PROT_SOCK;
+}
+#endif
+
__be32 inet_current_timestamp(void);
/* From inetpeer.c */
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 8e3f5b6..f1126686 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -96,6 +96,9 @@ struct netns_ipv4 {
/* Shall we try to damage output packets if routing dev changes? */
int sysctl_ip_dynaddr;
int sysctl_ip_early_demux;
+#ifdef CONFIG_LOWPORT_SYSCTL
+ int sysctl_ip_prot_sock;
+#endif
int sysctl_fwmark_reflect;
int sysctl_tcp_fwmark_accept;
diff --git a/net/Kconfig b/net/Kconfig
index a100500..946999c 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -109,6 +109,13 @@ config NETWORK_PHY_TIMESTAMPING
If you are unsure how to answer this question, answer N.
+config LOWPORT_SYSCTL
+ bool "Adjust reserved port range via sysctl"
+ depends on SYSCTL
+ help
+ This allows the administrator to adjust the reserved port range
+ using a sysctl.
+
menuconfig NETFILTER
bool "Network packet filtering framework (Netfilter)"
---help---
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index aae410b..c44ea78 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -479,7 +479,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
snum = ntohs(addr->sin_port);
err = -EACCES;
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
goto out;
@@ -1700,6 +1700,9 @@ static __net_init int inet_init_net(struct net *net)
net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
net->ipv4.sysctl_ip_dynaddr = 0;
net->ipv4.sysctl_ip_early_demux = 1;
+#ifdef CONFIG_LOWPORT_SYSCTL
+ net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
+#endif
return 0;
}
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 134d8e1..2167e3f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -35,6 +35,10 @@ static int ip_local_port_range_min[] = { 1, 1 };
static int ip_local_port_range_max[] = { 65535, 65535 };
static int tcp_adv_win_scale_min = -31;
static int tcp_adv_win_scale_max = 31;
+#ifdef CONFIG_LOWPORT_SYSCTL
+static int ip_protected_port_min;
+static int ip_protected_port_max = 65535;
+#endif
static int ip_ttl_min = 1;
static int ip_ttl_max = 255;
static int tcp_syn_retries_min = 1;
@@ -79,7 +83,17 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
if (write && ret == 0) {
+ /* Ensure that the upper limit is not smaller than the lower,
+ * and that the lower does not encroach upon the protected
+ * port limit.
+ */
+#ifdef CONFIG_LOWPORT_SYSCTL
+ if ((range[1] < range[0]) ||
+ (range[0] < net->ipv4.sysctl_ip_prot_sock))
+#else
+
if (range[1] < range[0])
+#endif
ret = -EINVAL;
else
set_local_port_range(net, range);
@@ -88,6 +102,42 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
return ret;
}
+#ifdef CONFIG_LOWPORT_SYSCTL
+/* Validate changes from /proc interface. */
+static int ipv4_protected_ports(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct net *net = container_of(table->data, struct net,
+ ipv4.sysctl_ip_prot_sock);
+ int ret;
+ int pports;
+ int range[2];
+ struct ctl_table tmp = {
+ .data = &pports,
+ .maxlen = sizeof(pports),
+ .mode = table->mode,
+ .extra1 = &ip_protected_port_min,
+ .extra2 = &ip_protected_port_max,
+ };
+
+ pports = net->ipv4.sysctl_ip_prot_sock;
+
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+ if (write && ret == 0) {
+ inet_get_local_port_range(net, &range[0], &range[1]);
+ /* Ensure that the local port range doesn't overlap with the
+ * protected port range.
+ */
+ if (range[0] < pports)
+ ret = -EINVAL;
+ else
+ net->ipv4.sysctl_ip_prot_sock = pports;
+ }
+
+ return ret;
+}
+#endif
static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
{
@@ -971,6 +1021,15 @@ static struct ctl_table ipv4_net_table[] = {
.extra2 = &one,
},
#endif
+#ifdef CONFIG_LOWPORT_SYSCTL
+ {
+ .procname = "ip_unprotected_port_start",
+ .maxlen = sizeof(int),
+ .data = &init_net.ipv4.sysctl_ip_prot_sock,
+ .mode = 0644,
+ .proc_handler = ipv4_protected_ports,
+ },
+#endif
{ }
};
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index aa42123..04db406 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -302,7 +302,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL;
snum = ntohs(addr->sin6_port);
- if (snum && snum < PROT_SOCK && !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+ if (snum && snum < inet_prot_sock(net) &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
lock_sock(sk);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 55e0169..8b7416f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -426,10 +426,9 @@ ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol
*/
svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, vport);
- if (svc == NULL
- && protocol == IPPROTO_TCP
- && atomic_read(&ipvs->ftpsvc_counter)
- && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {
+ if (!svc && protocol == IPPROTO_TCP &&
+ atomic_read(&ipvs->ftpsvc_counter) &&
+ (vport == FTPDATA || ntohs(vport) >= inet_prot_sock(ipvs->net))) {
/*
* Check if ftp service entry exists, the packet
* might belong to FTP data connections.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 318c678..2723f4a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -360,7 +360,7 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
}
}
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
@@ -1152,8 +1152,10 @@ static int __sctp_connect(struct sock *sk,
* accept new associations, but it SHOULD NOT
* be permitted to open new associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
- !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
+ if (ep->base.bind_addr.port <
+ inet_prot_sock(net) &&
+ !ns_capable(net->user_ns,
+ CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_free;
}
@@ -1818,7 +1820,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
* but it SHOULD NOT be permitted to open new
* associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
+ if (ep->base.bind_addr.port < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_unlock;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c7c6619..53cb6da 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4365,7 +4365,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
inet_get_local_port_range(sock_net(sk), &low, &high);
- if (snum < max(PROT_SOCK, low) || snum > high) {
+ if (snum < max(inet_prot_sock(sock_net(sk)), low) ||
+ snum > high) {
err = sel_netport_sid(sk->sk_protocol,
snum, &sid);
if (err)
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] Introduce a sysctl that modifies the value of PROT_SOCK.
2016-12-31 4:11 [PATCH] Introduce a sysctl that modifies the value of PROT_SOCK Krister Johansen
@ 2016-12-31 20:55 ` Stephen Hemminger
2017-01-04 10:19 ` Krister Johansen
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
0 siblings, 2 replies; 11+ messages in thread
From: Stephen Hemminger @ 2016-12-31 20:55 UTC (permalink / raw)
To: Krister Johansen; +Cc: David S. Miller, netdev
On Fri, 30 Dec 2016 20:11:11 -0800
Krister Johansen <kjlx@templeofstupid.com> wrote:
>
> +config LOWPORT_SYSCTL
> + bool "Adjust reserved port range via sysctl"
> + depends on SYSCTL
> + help
> + This allows the administrator to adjust the reserved port range
> + using a sysctl.
This looks like a good idea, and makes a lot of sense.
Please don't introduce yet another config option. All distro's will enable it anyway.
Having more config options doesn't help reliability or testability.
Do or do not, please no new config options.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] Introduce a sysctl that modifies the value of PROT_SOCK.
2016-12-31 20:55 ` Stephen Hemminger
@ 2017-01-04 10:19 ` Krister Johansen
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
1 sibling, 0 replies; 11+ messages in thread
From: Krister Johansen @ 2017-01-04 10:19 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Krister Johansen, David S. Miller, netdev
On Sat, Dec 31, 2016 at 12:55:05PM -0800, Stephen Hemminger wrote:
> On Fri, 30 Dec 2016 20:11:11 -0800
> Krister Johansen <kjlx@templeofstupid.com> wrote:
>
> >
> > +config LOWPORT_SYSCTL
> > + bool "Adjust reserved port range via sysctl"
> > + depends on SYSCTL
> > + help
> > + This allows the administrator to adjust the reserved port range
> > + using a sysctl.
>
> This looks like a good idea, and makes a lot of sense.
>
> Please don't introduce yet another config option. All distro's will enable it anyway.
> Having more config options doesn't help reliability or testability.
>
> Do or do not, please no new config options.
I'd be happy to take it out. It simplifies things for me. I had
anticipated that there would be objections to permitting software to get
around the current priviliged port restrictions, and thought that
perhaps as a compromise having it be compile time option would ease some
of those concerns.
-K
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2016-12-31 20:55 ` Stephen Hemminger
2017-01-04 10:19 ` Krister Johansen
@ 2017-01-12 6:52 ` Krister Johansen
2017-01-12 14:22 ` David Miller
` (2 more replies)
1 sibling, 3 replies; 11+ messages in thread
From: Krister Johansen @ 2017-01-12 6:52 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: David S. Miller, netdev
Add net.ipv4.ip_unprotected_port_start, which is a per namespace sysctl
that denotes the first unprotected inet port in the namespace. To
disable all protected ports set this to zero. It also checks for
overlap with the local port range. The protected and local range may
not overlap.
The use case for this change is to allow containerized processes to bind
to priviliged ports, but prevent them from ever being allowed to modify
their container's network configuration. The latter is accomplished by
ensuring that the network namespace is not a child of the user
namespace. This modification was needed to allow the container manager
to disable a namespace's priviliged port restrictions without exposing
control of the network namespace to processes in the user namespace.
Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
include/net/ip.h | 10 +++++++++
include/net/netns/ipv4.h | 1 +
net/ipv4/af_inet.c | 5 ++++-
net/ipv4/sysctl_net_ipv4.c | 50 +++++++++++++++++++++++++++++++++++++++++-
net/ipv6/af_inet6.c | 3 ++-
net/netfilter/ipvs/ip_vs_ctl.c | 7 +++---
net/sctp/socket.c | 10 +++++----
security/selinux/hooks.c | 3 ++-
8 files changed, 77 insertions(+), 12 deletions(-)
Changes v1 -> v2:
Remove LOWPORT_SYSCTL config option. This is now always enabled as long
as CONFIG_SYSCTL is.
diff --git a/include/net/ip.h b/include/net/ip.h
index ab6761a..bf264a8 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -263,11 +263,21 @@ static inline bool sysctl_dev_name_is_allowed(const char *name)
return strcmp(name, "default") != 0 && strcmp(name, "all") != 0;
}
+static inline int inet_prot_sock(struct net *net)
+{
+ return net->ipv4.sysctl_ip_prot_sock;
+}
+
#else
static inline int inet_is_local_reserved_port(struct net *net, int port)
{
return 0;
}
+
+static inline int inet_prot_sock(struct net *net)
+{
+ return PROT_SOCK;
+}
#endif
__be32 inet_current_timestamp(void);
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 8e3f5b6..e365732 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -135,6 +135,7 @@ struct netns_ipv4 {
#ifdef CONFIG_SYSCTL
unsigned long *sysctl_local_reserved_ports;
+ int sysctl_ip_prot_sock;
#endif
#ifdef CONFIG_IP_MROUTE
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index aae410b..28fe8da 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -479,7 +479,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
snum = ntohs(addr->sin_port);
err = -EACCES;
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
goto out;
@@ -1700,6 +1700,9 @@ static __net_init int inet_init_net(struct net *net)
net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
net->ipv4.sysctl_ip_dynaddr = 0;
net->ipv4.sysctl_ip_early_demux = 1;
+#ifdef CONFIG_SYSCTL
+ net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
+#endif
return 0;
}
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 134d8e1..6ad3b39 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -35,6 +35,8 @@ static int ip_local_port_range_min[] = { 1, 1 };
static int ip_local_port_range_max[] = { 65535, 65535 };
static int tcp_adv_win_scale_min = -31;
static int tcp_adv_win_scale_max = 31;
+static int ip_protected_port_min;
+static int ip_protected_port_max = 65535;
static int ip_ttl_min = 1;
static int ip_ttl_max = 255;
static int tcp_syn_retries_min = 1;
@@ -79,7 +81,12 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
if (write && ret == 0) {
- if (range[1] < range[0])
+ /* Ensure that the upper limit is not smaller than the lower,
+ * and that the lower does not encroach upon the protected
+ * port limit.
+ */
+ if ((range[1] < range[0]) ||
+ (range[0] < net->ipv4.sysctl_ip_prot_sock))
ret = -EINVAL;
else
set_local_port_range(net, range);
@@ -88,6 +95,40 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
return ret;
}
+/* Validate changes from /proc interface. */
+static int ipv4_protected_ports(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct net *net = container_of(table->data, struct net,
+ ipv4.sysctl_ip_prot_sock);
+ int ret;
+ int pports;
+ int range[2];
+ struct ctl_table tmp = {
+ .data = &pports,
+ .maxlen = sizeof(pports),
+ .mode = table->mode,
+ .extra1 = &ip_protected_port_min,
+ .extra2 = &ip_protected_port_max,
+ };
+
+ pports = net->ipv4.sysctl_ip_prot_sock;
+
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+ if (write && ret == 0) {
+ inet_get_local_port_range(net, &range[0], &range[1]);
+ /* Ensure that the local port range doesn't overlap with the
+ * protected port range.
+ */
+ if (range[0] < pports)
+ ret = -EINVAL;
+ else
+ net->ipv4.sysctl_ip_prot_sock = pports;
+ }
+
+ return ret;
+}
static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
{
@@ -971,6 +1012,13 @@ static struct ctl_table ipv4_net_table[] = {
.extra2 = &one,
},
#endif
+ {
+ .procname = "ip_unprotected_port_start",
+ .maxlen = sizeof(int),
+ .data = &init_net.ipv4.sysctl_ip_prot_sock,
+ .mode = 0644,
+ .proc_handler = ipv4_protected_ports,
+ },
{ }
};
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index aa42123..04db406 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -302,7 +302,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL;
snum = ntohs(addr->sin6_port);
- if (snum && snum < PROT_SOCK && !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+ if (snum && snum < inet_prot_sock(net) &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
lock_sock(sk);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 55e0169..8b7416f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -426,10 +426,9 @@ ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol
*/
svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, vport);
- if (svc == NULL
- && protocol == IPPROTO_TCP
- && atomic_read(&ipvs->ftpsvc_counter)
- && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {
+ if (!svc && protocol == IPPROTO_TCP &&
+ atomic_read(&ipvs->ftpsvc_counter) &&
+ (vport == FTPDATA || ntohs(vport) >= inet_prot_sock(ipvs->net))) {
/*
* Check if ftp service entry exists, the packet
* might belong to FTP data connections.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 318c678..2723f4a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -360,7 +360,7 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
}
}
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
@@ -1152,8 +1152,10 @@ static int __sctp_connect(struct sock *sk,
* accept new associations, but it SHOULD NOT
* be permitted to open new associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
- !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
+ if (ep->base.bind_addr.port <
+ inet_prot_sock(net) &&
+ !ns_capable(net->user_ns,
+ CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_free;
}
@@ -1818,7 +1820,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
* but it SHOULD NOT be permitted to open new
* associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
+ if (ep->base.bind_addr.port < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_unlock;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c7c6619..53cb6da 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4365,7 +4365,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
inet_get_local_port_range(sock_net(sk), &low, &high);
- if (snum < max(PROT_SOCK, low) || snum > high) {
+ if (snum < max(inet_prot_sock(sock_net(sk)), low) ||
+ snum > high) {
err = sel_netport_sid(sk->sk_protocol,
snum, &sid);
if (err)
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
@ 2017-01-12 14:22 ` David Miller
2017-01-14 0:13 ` Krister Johansen
2017-01-12 14:39 ` Eric Dumazet
2017-01-21 1:49 ` [PATCH v3 " Krister Johansen
2 siblings, 1 reply; 11+ messages in thread
From: David Miller @ 2017-01-12 14:22 UTC (permalink / raw)
To: kjlx; +Cc: stephen, netdev
From: Krister Johansen <kjlx@templeofstupid.com>
Date: Wed, 11 Jan 2017 22:52:25 -0800
> Add net.ipv4.ip_unprotected_port_start, which is a per namespace sysctl
> that denotes the first unprotected inet port in the namespace. To
> disable all protected ports set this to zero. It also checks for
> overlap with the local port range. The protected and local range may
> not overlap.
>
> The use case for this change is to allow containerized processes to bind
> to priviliged ports, but prevent them from ever being allowed to modify
> their container's network configuration. The latter is accomplished by
> ensuring that the network namespace is not a child of the user
> namespace. This modification was needed to allow the container manager
> to disable a namespace's priviliged port restrictions without exposing
> control of the network namespace to processes in the user namespace.
>
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
This is what CAP_NET_BIND_SERVICE is for, and why it is a separate
network privilege, please use it.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
2017-01-12 14:22 ` David Miller
@ 2017-01-12 14:39 ` Eric Dumazet
2017-01-14 0:11 ` Krister Johansen
2017-01-21 1:49 ` [PATCH v3 " Krister Johansen
2 siblings, 1 reply; 11+ messages in thread
From: Eric Dumazet @ 2017-01-12 14:39 UTC (permalink / raw)
To: Krister Johansen; +Cc: Stephen Hemminger, David S. Miller, netdev
On Wed, 2017-01-11 at 22:52 -0800, Krister Johansen wrote:
> Add net.ipv4.ip_unprotected_port_start, which is a per namespace sysctl
> that denotes the first unprotected inet port in the namespace. To
> disable all protected ports set this to zero. It also checks for
> overlap with the local port range. The protected and local range may
> not overlap.
>
> The use case for this change is to allow containerized processes to bind
> to priviliged ports, but prevent them from ever being allowed to modify
> their container's network configuration. The latter is accomplished by
> ensuring that the network namespace is not a child of the user
> namespace. This modification was needed to allow the container manager
> to disable a namespace's priviliged port restrictions without exposing
> control of the network namespace to processes in the user namespace.
>
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
> include/net/ip.h | 10 +++++++++
> include/net/netns/ipv4.h | 1 +
> net/ipv4/af_inet.c | 5 ++++-
> net/ipv4/sysctl_net_ipv4.c | 50 +++++++++++++++++++++++++++++++++++++++++-
> net/ipv6/af_inet6.c | 3 ++-
> net/netfilter/ipvs/ip_vs_ctl.c | 7 +++---
> net/sctp/socket.c | 10 +++++----
> security/selinux/hooks.c | 3 ++-
Adding a new sysctl without documentation is generally not accepted.
Please take a look at Documentation/networking/ip-sysctl.txt
BTW, sticking to 'unprivileged' ports might be better than 'unprotected'
which is vague.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-12 14:39 ` Eric Dumazet
@ 2017-01-14 0:11 ` Krister Johansen
0 siblings, 0 replies; 11+ messages in thread
From: Krister Johansen @ 2017-01-14 0:11 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Krister Johansen, Stephen Hemminger, David S. Miller, netdev
On Thu, Jan 12, 2017 at 06:39:57AM -0800, Eric Dumazet wrote:
> On Wed, 2017-01-11 at 22:52 -0800, Krister Johansen wrote:
> > Add net.ipv4.ip_unprotected_port_start, which is a per namespace sysctl
> > that denotes the first unprotected inet port in the namespace. To
> > disable all protected ports set this to zero. It also checks for
> > overlap with the local port range. The protected and local range may
> > not overlap.
> >
> > The use case for this change is to allow containerized processes to bind
> > to priviliged ports, but prevent them from ever being allowed to modify
> > their container's network configuration. The latter is accomplished by
> > ensuring that the network namespace is not a child of the user
> > namespace. This modification was needed to allow the container manager
> > to disable a namespace's priviliged port restrictions without exposing
> > control of the network namespace to processes in the user namespace.
> >
> > Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> > ---
> > include/net/ip.h | 10 +++++++++
> > include/net/netns/ipv4.h | 1 +
> > net/ipv4/af_inet.c | 5 ++++-
> > net/ipv4/sysctl_net_ipv4.c | 50 +++++++++++++++++++++++++++++++++++++++++-
> > net/ipv6/af_inet6.c | 3 ++-
> > net/netfilter/ipvs/ip_vs_ctl.c | 7 +++---
> > net/sctp/socket.c | 10 +++++----
> > security/selinux/hooks.c | 3 ++-
>
> Adding a new sysctl without documentation is generally not accepted.
>
> Please take a look at Documentation/networking/ip-sysctl.txt
Thanks for catching this. I'll add an entry to the documentation.
> BTW, sticking to 'unprivileged' ports might be better than 'unprotected'
> which is vague.
I don't have a strong preference about the naming. I'd be happy to
change it to 'unprivileged' instead.
-K
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-12 14:22 ` David Miller
@ 2017-01-14 0:13 ` Krister Johansen
0 siblings, 0 replies; 11+ messages in thread
From: Krister Johansen @ 2017-01-14 0:13 UTC (permalink / raw)
To: David Miller; +Cc: kjlx, stephen, netdev
On Thu, Jan 12, 2017 at 09:22:13AM -0500, David Miller wrote:
> From: Krister Johansen <kjlx@templeofstupid.com>
> > The use case for this change is to allow containerized processes to bind
> > to priviliged ports, but prevent them from ever being allowed to modify
> > their container's network configuration. The latter is accomplished by
> > ensuring that the network namespace is not a child of the user
> > namespace. This modification was needed to allow the container manager
> > to disable a namespace's priviliged port restrictions without exposing
> > control of the network namespace to processes in the user namespace.
>
> This is what CAP_NET_BIND_SERVICE is for, and why it is a separate
> network privilege, please use it.
It sounds like I may have done an inadequate job of explaining why I
took this approach instead of going the CAP_NET_BIND_SERVICE route.
In this scenario, the network namespace is created and configured first.
Then the containerized processed get placed into a separate user
namespace. This is so that the processes in the container, even if they
somehow manage to obtain extra privilege in the userns, can never modify
the network namespace.
The check in ns_capable() is looking at the priviliges of the user
namespace that created the netns and its parents. Even if I were to
grant a process in the container CAP_NET_BIND_SERVICE, ns_capable()
wouldn't recognize that as being a valid privilige for the netns.
If I were to invert the order of operations and create the userns before
the netns, then the capability would be recognized. However, that also
allows any potential privilege escalation in the userns to bring with it
the potential that an attacker can modify the container's network
configuration.
I'd much rather run the containers without privs, and without the userns
having rights to the netns, to mitigate the risk of an attacker being
able to alter the container's networking configuration.
-K
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
2017-01-12 14:22 ` David Miller
2017-01-12 14:39 ` Eric Dumazet
@ 2017-01-21 1:49 ` Krister Johansen
2017-01-23 20:39 ` David Miller
2017-01-24 17:11 ` David Miller
2 siblings, 2 replies; 11+ messages in thread
From: Krister Johansen @ 2017-01-21 1:49 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Stephen Hemminger, David S. Miller, netdev
Add net.ipv4.ip_unprivileged_port_start, which is a per namespace sysctl
that denotes the first unprivileged inet port in the namespace. To
disable all privileged ports set this to zero. It also checks for
overlap with the local port range. The privileged and local range may
not overlap.
The use case for this change is to allow containerized processes to bind
to priviliged ports, but prevent them from ever being allowed to modify
their container's network configuration. The latter is accomplished by
ensuring that the network namespace is not a child of the user
namespace. This modification was needed to allow the container manager
to disable a namespace's priviliged port restrictions without exposing
control of the network namespace to processes in the user namespace.
Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
Documentation/networking/ip-sysctl.txt | 9 ++++++
include/net/ip.h | 10 +++++++
include/net/netns/ipv4.h | 1 +
net/ipv4/af_inet.c | 5 +++-
net/ipv4/sysctl_net_ipv4.c | 50 +++++++++++++++++++++++++++++++++-
net/ipv6/af_inet6.c | 3 +-
net/netfilter/ipvs/ip_vs_ctl.c | 7 ++---
net/sctp/socket.c | 10 ++++---
security/selinux/hooks.c | 3 +-
9 files changed, 86 insertions(+), 12 deletions(-)
Changes v1 -> v2:
Remove LOWPORT_SYSCTL config option. This is now always enabled as long
as CONFIG_SYSCTL is.
Changes v2 -> v3:
Add documentation to ip-sysctl.txt.
Rename "protected" variables and functions to "privileged."
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index aa1bb49..17f2e77 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -822,6 +822,15 @@ ip_local_reserved_ports - list of comma separated ranges
Default: Empty
+ip_unprivileged_port_start - INTEGER
+ This is a per-namespace sysctl. It defines the first
+ unprivileged port in the network namespace. Privileged ports
+ require root or CAP_NET_BIND_SERVICE in order to bind to them.
+ To disable all privileged ports, set this to 0. It may not
+ overlap with the ip_local_reserved_ports range.
+
+ Default: 1024
+
ip_nonlocal_bind - BOOLEAN
If set, allows processes to bind() to non-local IP addresses,
which can be quite useful - but may break some applications.
diff --git a/include/net/ip.h b/include/net/ip.h
index ab6761a..bf264a8 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -263,11 +263,21 @@ static inline bool sysctl_dev_name_is_allowed(const char *name)
return strcmp(name, "default") != 0 && strcmp(name, "all") != 0;
}
+static inline int inet_prot_sock(struct net *net)
+{
+ return net->ipv4.sysctl_ip_prot_sock;
+}
+
#else
static inline int inet_is_local_reserved_port(struct net *net, int port)
{
return 0;
}
+
+static inline int inet_prot_sock(struct net *net)
+{
+ return PROT_SOCK;
+}
#endif
__be32 inet_current_timestamp(void);
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 8e3f5b6..e365732 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -135,6 +135,7 @@ struct netns_ipv4 {
#ifdef CONFIG_SYSCTL
unsigned long *sysctl_local_reserved_ports;
+ int sysctl_ip_prot_sock;
#endif
#ifdef CONFIG_IP_MROUTE
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index aae410b..28fe8da 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -479,7 +479,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
snum = ntohs(addr->sin_port);
err = -EACCES;
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
goto out;
@@ -1700,6 +1700,9 @@ static __net_init int inet_init_net(struct net *net)
net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
net->ipv4.sysctl_ip_dynaddr = 0;
net->ipv4.sysctl_ip_early_demux = 1;
+#ifdef CONFIG_SYSCTL
+ net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
+#endif
return 0;
}
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index c8d2836..1b86199 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -35,6 +35,8 @@ static int ip_local_port_range_min[] = { 1, 1 };
static int ip_local_port_range_max[] = { 65535, 65535 };
static int tcp_adv_win_scale_min = -31;
static int tcp_adv_win_scale_max = 31;
+static int ip_privileged_port_min;
+static int ip_privileged_port_max = 65535;
static int ip_ttl_min = 1;
static int ip_ttl_max = 255;
static int tcp_syn_retries_min = 1;
@@ -79,7 +81,12 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
if (write && ret == 0) {
- if (range[1] < range[0])
+ /* Ensure that the upper limit is not smaller than the lower,
+ * and that the lower does not encroach upon the privileged
+ * port limit.
+ */
+ if ((range[1] < range[0]) ||
+ (range[0] < net->ipv4.sysctl_ip_prot_sock))
ret = -EINVAL;
else
set_local_port_range(net, range);
@@ -88,6 +95,40 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
return ret;
}
+/* Validate changes from /proc interface. */
+static int ipv4_privileged_ports(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct net *net = container_of(table->data, struct net,
+ ipv4.sysctl_ip_prot_sock);
+ int ret;
+ int pports;
+ int range[2];
+ struct ctl_table tmp = {
+ .data = &pports,
+ .maxlen = sizeof(pports),
+ .mode = table->mode,
+ .extra1 = &ip_privileged_port_min,
+ .extra2 = &ip_privileged_port_max,
+ };
+
+ pports = net->ipv4.sysctl_ip_prot_sock;
+
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+ if (write && ret == 0) {
+ inet_get_local_port_range(net, &range[0], &range[1]);
+ /* Ensure that the local port range doesn't overlap with the
+ * privileged port range.
+ */
+ if (range[0] < pports)
+ ret = -EINVAL;
+ else
+ net->ipv4.sysctl_ip_prot_sock = pports;
+ }
+
+ return ret;
+}
static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
{
@@ -964,6 +1005,13 @@ static struct ctl_table ipv4_net_table[] = {
.extra2 = &one,
},
#endif
+ {
+ .procname = "ip_unprivileged_port_start",
+ .maxlen = sizeof(int),
+ .data = &init_net.ipv4.sysctl_ip_prot_sock,
+ .mode = 0644,
+ .proc_handler = ipv4_privileged_ports,
+ },
{ }
};
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index aa42123..04db406 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -302,7 +302,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL;
snum = ntohs(addr->sin6_port);
- if (snum && snum < PROT_SOCK && !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+ if (snum && snum < inet_prot_sock(net) &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
lock_sock(sk);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 55e0169..8b7416f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -426,10 +426,9 @@ ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol
*/
svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, vport);
- if (svc == NULL
- && protocol == IPPROTO_TCP
- && atomic_read(&ipvs->ftpsvc_counter)
- && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {
+ if (!svc && protocol == IPPROTO_TCP &&
+ atomic_read(&ipvs->ftpsvc_counter) &&
+ (vport == FTPDATA || ntohs(vport) >= inet_prot_sock(ipvs->net))) {
/*
* Check if ftp service entry exists, the packet
* might belong to FTP data connections.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index bee4dd3..d699d2c 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -360,7 +360,7 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
}
}
- if (snum && snum < PROT_SOCK &&
+ if (snum && snum < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
@@ -1152,8 +1152,10 @@ static int __sctp_connect(struct sock *sk,
* accept new associations, but it SHOULD NOT
* be permitted to open new associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
- !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
+ if (ep->base.bind_addr.port <
+ inet_prot_sock(net) &&
+ !ns_capable(net->user_ns,
+ CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_free;
}
@@ -1818,7 +1820,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
* but it SHOULD NOT be permitted to open new
* associations.
*/
- if (ep->base.bind_addr.port < PROT_SOCK &&
+ if (ep->base.bind_addr.port < inet_prot_sock(net) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_unlock;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c7c6619..53cb6da 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4365,7 +4365,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
inet_get_local_port_range(sock_net(sk), &low, &high);
- if (snum < max(PROT_SOCK, low) || snum > high) {
+ if (snum < max(inet_prot_sock(sock_net(sk)), low) ||
+ snum > high) {
err = sel_netport_sid(sk->sk_protocol,
snum, &sid);
if (err)
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-21 1:49 ` [PATCH v3 " Krister Johansen
@ 2017-01-23 20:39 ` David Miller
2017-01-24 17:11 ` David Miller
1 sibling, 0 replies; 11+ messages in thread
From: David Miller @ 2017-01-23 20:39 UTC (permalink / raw)
To: kjlx; +Cc: eric.dumazet, stephen, netdev
From: Krister Johansen <kjlx@templeofstupid.com>
Date: Fri, 20 Jan 2017 17:49:11 -0800
> Add net.ipv4.ip_unprivileged_port_start, which is a per namespace sysctl
> that denotes the first unprivileged inet port in the namespace. To
> disable all privileged ports set this to zero. It also checks for
> overlap with the local port range. The privileged and local range may
> not overlap.
>
> The use case for this change is to allow containerized processes to bind
> to priviliged ports, but prevent them from ever being allowed to modify
> their container's network configuration. The latter is accomplished by
> ensuring that the network namespace is not a child of the user
> namespace. This modification was needed to allow the container manager
> to disable a namespace's priviliged port restrictions without exposing
> control of the network namespace to processes in the user namespace.
>
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
I'm not ignoring this change, I just want to think about it some more.
Just FYI...
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
2017-01-21 1:49 ` [PATCH v3 " Krister Johansen
2017-01-23 20:39 ` David Miller
@ 2017-01-24 17:11 ` David Miller
1 sibling, 0 replies; 11+ messages in thread
From: David Miller @ 2017-01-24 17:11 UTC (permalink / raw)
To: kjlx; +Cc: eric.dumazet, stephen, netdev
From: Krister Johansen <kjlx@templeofstupid.com>
Date: Fri, 20 Jan 2017 17:49:11 -0800
> Add net.ipv4.ip_unprivileged_port_start, which is a per namespace sysctl
> that denotes the first unprivileged inet port in the namespace. To
> disable all privileged ports set this to zero. It also checks for
> overlap with the local port range. The privileged and local range may
> not overlap.
>
> The use case for this change is to allow containerized processes to bind
> to priviliged ports, but prevent them from ever being allowed to modify
> their container's network configuration. The latter is accomplished by
> ensuring that the network namespace is not a child of the user
> namespace. This modification was needed to allow the container manager
> to disable a namespace's priviliged port restrictions without exposing
> control of the network namespace to processes in the user namespace.
>
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
Applied, thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2017-01-24 17:11 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-12-31 4:11 [PATCH] Introduce a sysctl that modifies the value of PROT_SOCK Krister Johansen
2016-12-31 20:55 ` Stephen Hemminger
2017-01-04 10:19 ` Krister Johansen
2017-01-12 6:52 ` [PATCH v2 net-next] " Krister Johansen
2017-01-12 14:22 ` David Miller
2017-01-14 0:13 ` Krister Johansen
2017-01-12 14:39 ` Eric Dumazet
2017-01-14 0:11 ` Krister Johansen
2017-01-21 1:49 ` [PATCH v3 " Krister Johansen
2017-01-23 20:39 ` David Miller
2017-01-24 17:11 ` David Miller
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).