The "filter_class" option was first introduced as part of the effort to support
the Homenet Control Protocol (HNCP, RFC7368, RFC7788). At first it was
hardcoded to filter out the user class "HOMENET", and later it was made
configurable, but there appears to be no other use-cases than Homenet. There's
also legacy comments in the odhcpd code pointing at filtering out homenet user
classes (the comments are misleading since "filter_class" is not set by
default).
Homenet is effectively dead. The "hnetd" daemon last saw any code changes in
2018, doesn't compile with current versions of GCC, and was removed from
OpenWrt altogether in 2025 [1].
The OpenWrt wiki also notes [2] that HNCP is effectively dead, and that the
upstream website [3] is gone.
Searching for the Homenet Control Protocol mostly returns results pointing
either:
a) at the standards
b) at the OpenWrt/hnetd project
c) at various posts stating that it is dead
The IETF working group for Homenet is no more, and Éric Vyncke (Area Director
for the Internet Area of the IETF) mentioned wanting to avoid Homenet pitfalls
[4] (page 11) in the future of IPv6 autoconfig.
The current implementation is also contrary to the spirit of the RFCs
introducing the option (RFC3004 - DHCPv4; RFC8415, §21.15 - DHCPv6) where it is
described (example from RFC8415) as follows:
The information contained in the data area of this option is
contained in one or more opaque fields that represent the user class
or classes of which the client is a member. A server selects
configuration information for the client based on the classes
identified in this option. For example, the User Class option can be
used to configure all clients of people in the accounting department
with a different printer than clients of people in the marketing
department.
I.e., it's meant to be used in a manner similar to "tags" in dnsmasq.
Finally, the option has been undocumented for the whole existance of odhcpd.
So, remove this option.
[1] https://github.com/openwrt/routing/commit/
85b868b3413a29da0bd6ecd3518c2d34a6ffb788
[2] https://openwrt.org/docs/guide-user/network/zeroconfig/hncp_configuration
[3] https://web.archive.org/web/
20180831161552/http://homewrt.org/start
[4] https://ripe87.ripe.net/wp-content/uploads/presentations/102-
20231130-RIPE-87-IPv6-from-IETF.pdf
Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/294
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
| ntp |list |`<local address>`| NTP servers to announce accepts IPv4 and IPv6 |
| ra_management |string | - | TBD |
| upstream |list | - | TBD |
-| filter_class |string | - | TBD |
| pd_manager |string | - | TBD |
| pd_cer |string | - | TBD |
| ra_advrouter |bool | - | TBD |
IFACE_ATTR_DNR,
IFACE_ATTR_DNS_SERVICE,
IFACE_ATTR_DOMAIN,
- IFACE_ATTR_FILTER_CLASS,
IFACE_ATTR_DHCPV4_FORCERECONF,
IFACE_ATTR_DHCPV6_RAW,
IFACE_ATTR_DHCPV6_ASSIGNALL,
[IFACE_ATTR_DNR] = { .name = "dnr", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DNS_SERVICE] = { .name = "dns_service", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
- [IFACE_ATTR_FILTER_CLASS] = { .name = "filter_class", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DHCPV4_FORCERECONF] = { .name = "dhcpv4_forcereconf", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_RAW] = { .name = "dhcpv6_raw", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DHCPV6_ASSIGNALL] = { .name ="dhcpv6_assignall", .type = BLOBMSG_TYPE_BOOL },
free(iface->dhcpv4_router);
free(iface->dhcpv4_dns);
free(iface->dhcpv6_raw);
- free(iface->filter_class);
free(iface->dhcpv4_ntp);
free(iface->dhcpv6_ntp);
free(iface->dhcpv6_sntp);
}
}
- if ((c = tb[IFACE_ATTR_FILTER_CLASS])) {
- iface->filter_class = realloc(iface->filter_class, blobmsg_data_len(c) + 1);
- memcpy(iface->filter_class, blobmsg_get_string(c), blobmsg_data_len(c) + 1);
- }
-
if ((c = tb[IFACE_ATTR_DHCPV4_FORCERECONF]))
iface->dhcpv4_forcereconf = blobmsg_get_bool(c);
req_opts_len = opt->len;
}
break;
- case DHCPV4_OPT_USER_CLASS:
- if (iface->filter_class) {
- uint8_t *c = opt->data, *cend = &opt->data[opt->len];
- for (; c < cend && &c[*c] < cend; c = &c[1 + *c]) {
- size_t elen = strlen(iface->filter_class);
- if (*c == elen && !memcmp(&c[1], iface->filter_class, elen))
- return; // Ignore from homenet
- }
- }
- break;
case DHCPV4_OPT_LEASETIME:
if (opt->len == 4) {
memcpy(&req_leasetime, opt->data, 4);
if (olen != ntohs(dest.serverid_length) ||
memcmp(odata, &dest.serverid_buf, olen))
return; /* Not for us */
- } else if (iface->filter_class && otype == DHCPV6_OPT_USER_CLASS) {
- uint8_t *c = odata, *cend = &odata[olen];
- for (; &c[2] <= cend && &c[2 + (c[0] << 8) + c[1]] <= cend; c = &c[2 + (c[0] << 8) + c[1]]) {
- size_t elen = strlen(iface->filter_class);
- if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], iface->filter_class, elen))
- return; /* Ignore from homenet */
- }
} else if (otype == DHCPV6_OPT_IA_PD) {
#ifdef EXT_CER_ID
iov[IOV_CERID].iov_len = sizeof(cerid);
char *upstream;
size_t upstream_len;
- char *filter_class;
-
// NTP
struct in_addr *dhcpv4_ntp;
size_t dhcpv4_ntp_cnt;