1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
From: Stephen Hemminger <stephen@networkplumber.org>
Date: Mon, 29 Apr 2013 18:50:15 -0700
Subject: [PATCH] VyOS: Add linkstate IP device attribute
Backport of earlier Vyatta patch.
(cherry picked from commit 7c5a851086686be14ae937c80d6cee34814dbefc)
Refreshed against Linux 6.18.x:
* include/uapi/linux/ipv6.h gained DEVCONF_FORCE_FORWARDING; insert
DEVCONF_LINK_FILTER after it so the new sentinel keeps a stable value.
* include/linux/ipv6.h grew ra_honor_pio_life / ra_honor_pio_pflag
members before sysctl_header.
* net/ipv6/addrconf.c::ipv6_store_devconf() now uses READ_ONCE() and
publishes DEVCONF_FORCE_FORWARDING; use READ_ONCE() for link_filter
and append after force_forwarding.
* net/ipv6/route.c context shifted; rt6_link_filter() unchanged.
---
Documentation/networking/ip-sysctl.rst | 11 +++++++++++
include/linux/inetdevice.h | 1 +
include/linux/ipv6.h | 1 +
include/uapi/linux/ip.h | 1 +
include/uapi/linux/ipv6.h | 1 +
net/ipv4/devinet.c | 1 +
net/ipv6/addrconf.c | 8 ++++++++
net/ipv6/route.c | 10 ++++++++++
8 files changed, 34 insertions(+)
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 7a637e87005f..982c76d0b0ab 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -2042,6 +2042,17 @@ src_valid_mark - BOOLEAN
Default value is 0.
+link_filter - INTEGER
+ 0 - Allow packets to be received for the address on this interface
+ even if interface is disabled or no carrier.
+ 1 - Ignore packets received if interface associated with the incoming
+ address is down.
+ 2 - Ignore packets received if interface associated with the incoming
+ address is down or has no carrier.
+
+ Default value is 0. Note that some distributions enable it
+ in startup scripts.
+
arp_filter - BOOLEAN
- 1 - Allows you to have multiple network interfaces on the same
subnet, and have the ARPs for each interface be answered
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index dccbeb25f701..b16f70e491d6 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -139,6 +139,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
#define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY)
#define IN_DEV_ARP_EVICT_NOCARRIER(in_dev) IN_DEV_ANDCONF((in_dev), \
ARP_EVICT_NOCARRIER)
+#define IN_DEV_LINKFILTER(in_dev) IN_DEV_MAXCONF((in_dev), LINKFILTER)
struct in_ifaddr {
struct hlist_node addr_lst;
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 7294e4e89b79..5243afbd98b8 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -91,6 +91,7 @@ struct ipv6_devconf {
__u8 ndisc_evict_nocarrier;
__u8 ra_honor_pio_life;
__u8 ra_honor_pio_pflag;
+ __s32 link_filter;
struct ctl_table_header *sysctl_header;
};
diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h
index 5bd7ce934d74..cfe694a9810f 100644
--- a/include/uapi/linux/ip.h
+++ b/include/uapi/linux/ip.h
@@ -189,6 +189,7 @@ enum
IPV4_DEVCONF_DROP_GRATUITOUS_ARP,
IPV4_DEVCONF_BC_FORWARDING,
IPV4_DEVCONF_ARP_EVICT_NOCARRIER,
+ IPV4_DEVCONF_LINKFILTER,
__IPV4_DEVCONF_MAX
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index d4d3ae774b26..8495f1b69a64 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -200,6 +200,7 @@ enum {
DEVCONF_ACCEPT_UNTRACKED_NA,
DEVCONF_ACCEPT_RA_MIN_LFT,
DEVCONF_FORCE_FORWARDING,
+ DEVCONF_LINK_FILTER,
DEVCONF_MAX
};
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 942a887bf089..e6df2bfc4e40 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -2652,6 +2652,7 @@ static struct devinet_sysctl_table {
"route_localnet"),
DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
"drop_unicast_in_l2_multicast"),
+ DEVINET_SYSCTL_RW_ENTRY(LINKFILTER, "link_filter"),
},
};
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index b2e1328371d3..769d4e1f5cbf 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5718,6 +5718,7 @@ static void ipv6_store_devconf(const struct ipv6_devconf *cnf,
READ_ONCE(cnf->accept_untracked_na);
array[DEVCONF_ACCEPT_RA_MIN_LFT] = READ_ONCE(cnf->accept_ra_min_lft);
array[DEVCONF_FORCE_FORWARDING] = READ_ONCE(cnf->force_forwarding);
+ array[DEVCONF_LINK_FILTER] = READ_ONCE(cnf->link_filter);
}
static inline size_t inet6_ifla6_size(void)
@@ -7253,6 +7254,13 @@ static const struct ctl_table addrconf_sysctl[] = {
.extra1 = (void *)SYSCTL_ZERO,
.extra2 = (void *)SYSCTL_ONE,
},
+ {
+ .procname = "link_filter",
+ .data = &ipv6_devconf.link_filter,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
{
.procname = "ioam6_id",
.data = &ipv6_devconf.ioam6_id,
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f89220929c4e..8f92ef14d884 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -717,6 +717,14 @@ static inline void rt6_probe(struct fib6_nh *fib6_nh)
}
#endif
+static inline int rt6_link_filter(const struct fib6_nh *nh)
+{
+ const struct net_device *dev = nh->fib_nh_dev;
+ int linkf = __in6_dev_get(dev)->cnf.link_filter;
+ return (linkf && !netif_running(dev))
+ || (linkf > 1 && !netif_carrier_ok(dev));
+}
+
/*
* Default Router Selection (RFC 2461 6.3.6)
*/
@@ -758,6 +766,8 @@ static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
if (!m && (strict & RT6_LOOKUP_F_IFACE))
return RT6_NUD_FAIL_HARD;
+ if (rt6_link_filter(nh))
+ return -1;
#ifdef CONFIG_IPV6_ROUTER_PREF
m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
#endif
--
2.39.5
|