From 59124bbc26bf0211723b3eb5b5186d3dc7b72b0c Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 17:20:32 +0300 Subject: ipoe: fix ipoe_wlock double release in IPOE_CMD_DELETE ipoe_nl_cmd_delete() drops ipoe_wlock before sleeping in synchronize_rcu() and taking rtnl via unregister_netdev(), then falls through into the out_unlock label and releases it a second time. Every successful session delete therefore increments the semaphore count by one. Since the count only ever grows, ipoe_wlock stops providing mutual exclusion after a handful of teardowns: with the count at N, up to N+1 writers may hold it simultaneously. This is currently masked because ipoe_nl_family does not set parallel_ops, so genetlink serialises every .doit/.dumpit under genl_mutex and no two writers can overlap in practice. It turns into a real race on the session hash lists as soon as that changes, or as soon as a writer is introduced outside the genl handlers. It also silently defeats the barrier in ipoe_fini(): the down()/up() pair meant to wait for an in-flight writer is satisfied immediately by the leaked count and waits for nothing. Return directly after unregister_netdev() so the success path releases the lock exactly once. The early up() is deliberate and stays where it is - holding a sleeping semaphore across unregister_netdev() would nest ipoe_wlock inside rtnl, while ipoe_create() takes rtnl first and ipoe_wlock afterwards. Error paths, return values and lock ordering are unchanged. --- drivers/ipoe/ipoe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/ipoe') diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 2829e6ac..3bf8fef7 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -1399,6 +1399,8 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) if (ses->u.hwaddr_u) list_del_rcu(&ses->entry3); + /* drop the lock before sleeping in synchronize_rcu() and taking rtnl + * in unregister_netdev() */ up(&ipoe_wlock); synchronize_rcu(); @@ -1411,7 +1413,7 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) unregister_netdev(ses->dev); - ret = 0; + return 0; out_unlock: up(&ipoe_wlock); -- cgit v1.2.3 From 4fb21bfd967ee834d2b5d5ea11d0838002d6ac1a Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 17:46:44 +0300 Subject: ipoe: do not skip a session when a dump spans several messages ipoe_nl_cmd_dump_sessions() increments idx before calling fill_info(), so once fill_info() fails because the skb is full, idx already points past the session that did not fit. cb->args[0] is set to that value and the next round resumes one entry too far, dropping the session from the dump entirely - one lost session per message boundary, roughly one in every 90 at the current record size. Step idx back before leaving the loop. --- drivers/ipoe/ipoe.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/ipoe') diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 3bf8fef7..73926994 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -1579,7 +1579,11 @@ static int ipoe_nl_cmd_dump_sessions(struct sk_buff *skb, struct netlink_callbac #else if (fill_info(skb, ses, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq) < 0) #endif + { + /* this one did not fit, resume from it next time */ + idx--; break; + } } up(&ipoe_wlock); -- cgit v1.2.3 From 6693d18ae0232da2c6febc0decf099e4fd064381 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 17:47:49 +0300 Subject: ipoe: flush sessions left by a previous instance with a single command On startup accel-pppd is expected to drop everything a previous instance left behind in the kernel. For sessions it did so by dumping them with IPOE_CMD_GET and sending one IPOE_CMD_DELETE per ifindex. That dump was written for the session backup code removed in 1972a7e5c and was never adapted to its new, destructive role: - it was issued on the socket subscribed to the packet multicast group, so it competed with the notifications that the still attached stale rx handlers keep generating; on a loaded box the receive buffer overruns and rtnl_dump_filter() gives up with ENOBUFS, - its return value was discarded, so such a failure was silent, - it ran before the interfaces were detached, which is what produces those notifications in the first place, - it was skipped entirely when the multicast group could not be resolved, again with nothing but a warning about packet handling, - and every session cost a socket, a round trip, a grace period and a full unregister_netdev(). Whatever it missed stays in the kernel forever: the daemon has no record of those sessions, so nothing ever deletes them, and every subscriber later assigned one of their addresses is refused with EEXIST by IPOE_CMD_MODIFY. Add IPOE_CMD_FLUSH, which unlinks all sessions in one go, waits for a single grace period and unregisters the devices with unregister_netdevice_many(), and use it instead. The old path is kept as a fallback for a module predating the command, which is reported as EOPNOTSUPP, and now checks its return value and uses a private socket. Reorder init() so the interfaces are detached before the multicast group is joined, and so the flush also runs when only the group lookup failed. --- accel-pppd/ctrl/ipoe/ipoe.h | 3 +- accel-pppd/ctrl/ipoe/ipoe_netlink.c | 101 +++++++++++++++++++++++++++++++----- drivers/ipoe/ipoe.c | 54 +++++++++++++++++++ drivers/ipoe/ipoe.h | 1 + 4 files changed, 146 insertions(+), 13 deletions(-) (limited to 'drivers/ipoe') diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index 37c26a63..2bd3a14f 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -164,7 +164,8 @@ void ipoe_nl_delete_interfaces(void); int ipoe_nl_create(); void ipoe_nl_delete(int ifindex); int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, uint32_t gw, int link_ifindex, uint8_t *hwaddr); -void ipoe_nl_get_sessions(struct list_head *list); +int ipoe_nl_get_sessions(struct list_head *list); +int ipoe_nl_flush_sessions(void); int ipoe_nl_add_exclude(uint32_t addr, int mask); void ipoe_nl_del_exclude(uint32_t addr); int ipoe_nl_add_net(uint32_t addr, int mask); diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c index 94b7f42a..6eee2dca 100644 --- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c @@ -410,17 +410,23 @@ static int dump_session(const struct sockaddr_nl *addr, struct nlmsghdr *n, void return 0; } -void ipoe_nl_get_sessions(struct list_head *list) +int ipoe_nl_get_sessions(struct list_head *list) { + struct rtnl_handle rth; struct nlmsghdr *nlh; struct genlmsghdr *ghdr; struct { struct nlmsghdr n; char buf[1024]; } req; + int ret; - if (rth.fd == -1) - return; + /* a private socket, so that the dump does not have to compete with + * the packet notifications delivered to the multicast one */ + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_error("ipoe: cannot open generic netlink socket\n"); + return -1; + } memset(&req, 0, sizeof(req)); @@ -434,11 +440,51 @@ void ipoe_nl_get_sessions(struct list_head *list) ghdr->cmd = IPOE_CMD_GET; if (rtnl_send(&rth, (char *)nlh, nlh->nlmsg_len) < 0) { - log_emerg("ipoe: failed to send dump request: %s\n", strerror(errno)); - return; + log_error("ipoe: failed to send dump request: %s\n", strerror(errno)); + rtnl_close(&rth); + return -1; + } + + ret = rtnl_dump_filter(&rth, dump_session, list, NULL, NULL); + + rtnl_close(&rth); + + return ret; +} + +int ipoe_nl_flush_sessions(void) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + struct { + struct nlmsghdr n; + char buf[128]; + } req; + int ret = 0; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_error("ipoe: cannot open generic netlink socket\n"); + errno = EIO; + return -1; } - rtnl_dump_filter(&rth, dump_session, list, NULL, NULL); + memset(&req, 0, sizeof(req)); + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = ipoe_genl_id; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = IPOE_CMD_FLUSH; + + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0) + ret = -1; + + rtnl_close(&rth); + + return ret; } void ipoe_nl_delete(int ifindex) @@ -479,7 +525,10 @@ static void delete_sessions() struct ipoe_session_info *info; LIST_HEAD(ds_list); - ipoe_nl_get_sessions(&ds_list); + + if (ipoe_nl_get_sessions(&ds_list)) + log_error("ipoe: failed to enumerate sessions left by a previous" + " instance, some of them are not removed\n"); while (!list_empty(&ds_list)) { info = list_entry(ds_list.next, typeof(*info), entry); @@ -489,6 +538,22 @@ static void delete_sessions() } } +static void flush_sessions() +{ + if (!ipoe_nl_flush_sessions()) + return; + + if (errno == EOPNOTSUPP) { + log_warn("ipoe: loaded ipoe module does not support IPOE_CMD_FLUSH," + " removing sessions one by one, reload the module to fix\n"); + delete_sessions(); + return; + } + + log_error("ipoe: failed to remove sessions left by a previous instance:" + " %s\n", strerror(errno)); +} + static void ipoe_up_handler(const struct sockaddr_nl *addr, struct nlmsghdr *h) { struct rtattr *tb[PKT_ATTR_MAX + 1]; @@ -648,6 +713,23 @@ static void init(void) log_warn("failed to load ipoe module\n"); mcg_id = genl_resolve_mcg(IPOE_GENL_NAME, IPOE_GENL_MCG_PKT, &ipoe_genl_id); + + if (!ipoe_genl_id) { + log_error("ipoe: cannot resolve netlink family, state left by a" + " previous instance is not removed\n"); + rth.fd = -1; + return; + } + + /* Drop everything a previous instance may have left in the kernel. + * The interfaces go first: while their rx handlers are still attached + * the module keeps reporting unclassified packets, and once we join + * the multicast group that traffic competes with our own replies. */ + ipoe_nl_delete_interfaces(); + flush_sessions(); + ipoe_nl_del_exclude(0); + ipoe_nl_del_net(0); + if (mcg_id == -1) { log_warn("ipoe: unclassified packet handling is disabled\n"); rth.fd = -1; @@ -660,11 +742,6 @@ static void init(void) return; } - delete_sessions(); - ipoe_nl_del_exclude(0); - ipoe_nl_del_net(0); - ipoe_nl_delete_interfaces(); - fcntl(rth.fd, F_SETFL, O_NONBLOCK); fcntl(rth.fd, F_SETFD, fcntl(rth.fd, F_GETFD) | FD_CLOEXEC); diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 73926994..8f1c852b 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -1420,6 +1420,52 @@ out_unlock: return ret; } +static int ipoe_nl_cmd_flush(struct sk_buff *skb, struct genl_info *info) +{ + struct ipoe_session *ses; + LIST_HEAD(list); + LIST_HEAD(kill_list); + + down(&ipoe_wlock); + + list_splice_init(&ipoe_list2, &list); + + list_for_each_entry(ses, &list, entry2) { + if (ses->peer_addr) + list_del_rcu(&ses->entry); + if (ses->u.hwaddr_u) + list_del_rcu(&ses->entry3); + } + + up(&ipoe_wlock); + + if (list_empty(&list)) + return 0; + + /* a single grace period covers the whole batch */ + synchronize_rcu(); + + list_for_each_entry(ses, &list, entry2) { + while (atomic_read(&ses->refs)) + schedule_timeout_uninterruptible(1); + + if (ses->link_dev) { + dev_put(ses->link_dev); + ses->link_dev = NULL; + } + } + + rtnl_lock(); + list_for_each_entry(ses, &list, entry2) + unregister_netdevice_queue(ses->dev, &kill_list); + unregister_netdevice_many(&kill_list); + rtnl_unlock(); + + /* the sessions are freed by now, do not touch 'list' again */ + + return 0; +} + static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info) { int ret = -EINVAL, r = 0; @@ -1894,6 +1940,14 @@ static const struct genl_ops ipoe_nl_ops[] = { .flags = GENL_ADMIN_PERM, #if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0) .policy = ipoe_nl_policy, +#endif + }, + { + .cmd = IPOE_CMD_FLUSH, + .doit = ipoe_nl_cmd_flush, + .flags = GENL_ADMIN_PERM, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0) + .policy = ipoe_nl_policy, #endif }, }; diff --git a/drivers/ipoe/ipoe.h b/drivers/ipoe/ipoe.h index 4097e2da..2d041c50 100644 --- a/drivers/ipoe/ipoe.h +++ b/drivers/ipoe/ipoe.h @@ -16,6 +16,7 @@ enum { IPOE_CMD_DEL_EXCLUDE, IPOE_CMD_ADD_NET, IPOE_CMD_DEL_NET, + IPOE_CMD_FLUSH, __IPOE_CMD_MAX, }; -- cgit v1.2.3 From 4969b65c3f55c739a0c9178086f42ba3de906119 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 18:12:56 +0300 Subject: ipoe: allow session interfaces to be removed with ip link del Sessions left in the kernel by a dead daemon could only be got rid of by restarting accel-pppd, which flushes all of them and so drops everyone still online, or by unloading the module. Neither is of much use when a single stale interface is holding an address and every subscriber that is later handed it is refused with EEXIST. Register rtnl_link_ops, so that RTM_DELLINK reaches the driver: ip link show type ipoe enumerate them ip -d link show ipoe42 reports kind "ipoe" ip link del ipoe42 remove a single one newlink returns EOPNOTSUPP rather than being left out. A session carries private state that IPOE_CMD_CREATE sets up, and a kernel that falls back to register_netdevice() when newlink is NULL would hand out a device with zeroed private state and no percpu counters, which oopses on the first packet or on ip -s link show. dellink runs under rtnl and outside genl_mutex, and is therefore the first writer in this driver that does not serialise against the genl handlers: it can unlink a session that IPOE_CMD_DELETE is about to unlink as well, since the device stays registered until both are done. Add ipoe_session.dying, set under ipoe_wlock by whichever path starts the teardown, and let the other one bail out. That leaves rtnl nested inside ipoe_wlock, which is safe because no path does it the other way round: ipoe_create() releases rtnl before taking ipoe_wlock, ipoe_nl_cmd_delete() releases ipoe_wlock before calling unregister_netdev(), and the interface commands use rtnl alone. dellink does have to sleep in synchronize_rcu() and while draining the session refcount with rtnl held, which the genl path avoids by deferring both until after it has dropped everything. A session left behind by a dead daemon is indistinguishable from one that is still in use - same peer address, same lists, same flags - so the removal cannot be refused on state alone. Warn instead, and only when somebody is still subscribed to the packet multicast group, so that clearing stale interfaces on a box where accel-pppd is not running stays quiet. --- drivers/ipoe/ipoe.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'drivers/ipoe') diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 8f1c852b..a9b11fa2 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -104,6 +104,10 @@ struct ipoe_session { atomic_t refs; + /* set under ipoe_wlock by whoever starts tearing the session down, + * so that the genl and the rtnetlink path do not do it twice */ + unsigned int dying:1; + struct ipoe_stats __percpu *rx_stats; struct ipoe_stats __percpu *tx_stats; }; @@ -1116,6 +1120,83 @@ static const struct header_ops ipoe_hard_header_ops = { .cache_update = eth_header_cache_update, }; +/* Is anybody subscribed to our packet group, i.e. is a control daemon + * running at all? */ +static int ipoe_ctrl_attached(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) && RHEL_MAJOR < 7 + return netlink_has_listeners(init_net.genl_sock, ipoe_nl_mcg.id); +#else + return genl_has_listeners(&ipoe_nl_family, &init_net, 0); +#endif +} + +/* Sessions are created through IPOE_CMD_CREATE, which also sets up the + * private state. A device made by rtnetlink would have none of it, so + * refuse 'ip link add ... type ipoe' explicitly. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) +static int ipoe_newlink(struct net_device *dev, + struct rtnl_newlink_params *params, + struct netlink_ext_ack *extack) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) +static int ipoe_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +#else +static int ipoe_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +#endif +{ + return -EOPNOTSUPP; +} + +/* Called by rtnetlink with rtnl held, so the teardown ipoe_nl_cmd_delete() + * does after dropping ipoe_wlock has to happen here as well. */ +static void ipoe_dellink(struct net_device *dev, struct list_head *head) +{ + struct ipoe_session *ses = netdev_priv(dev); + + down(&ipoe_wlock); + + if (ses->dying) { + /* the genl path is already removing it, it will unregister + * the device itself once it gets rtnl */ + up(&ipoe_wlock); + return; + } + + ses->dying = 1; + + if (ses->peer_addr) + list_del_rcu(&ses->entry); + list_del(&ses->entry2); + if (ses->u.hwaddr_u) + list_del_rcu(&ses->entry3); + + up(&ipoe_wlock); + + /* A session left behind by a dead daemon looks exactly like one that + * is still in use, so removing it can not be refused on state alone. + * Complain only if somebody is still subscribed to our multicast + * group, which means a daemon is around to be surprised by it. */ + if (ses->peer_addr && ipoe_ctrl_attached()) + pr_warn("ipoe: %s: removed through rtnetlink while bound to %pI4" + " and a control daemon is attached\n", + dev->name, &ses->peer_addr); + + synchronize_rcu(); + + while (atomic_read(&ses->refs)) + schedule_timeout_uninterruptible(1); + + if (ses->link_dev) { + dev_put(ses->link_dev); + ses->link_dev = NULL; + } + + unregister_netdevice_queue(dev, head); +} + static void ipoe_netdev_setup(struct net_device *dev) { dev->netdev_ops = &ipoe_netdev_ops; @@ -1145,6 +1226,14 @@ static void ipoe_netdev_setup(struct net_device *dev) dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; } +static struct rtnl_link_ops ipoe_link_ops __read_mostly = { + .kind = "ipoe", + .priv_size = sizeof(struct ipoe_session), + .setup = ipoe_netdev_setup, + .newlink = ipoe_newlink, + .dellink = ipoe_dellink, +}; + static int ipoe_create(__be32 peer_addr, __be32 addr, __be32 gw, int ifindex, const __u8 *hwaddr) { struct ipoe_session *ses; @@ -1220,6 +1309,7 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, __be32 gw, int ifindex, co }*/ dev->tx_queue_len = 100; + dev->rtnl_link_ops = &ipoe_link_ops; rtnl_lock(); r = register_netdevice(dev); @@ -1393,6 +1483,14 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) //pr_info("ipoe: delete %08x\n", ses->peer_addr); + if (ses->dying) { + /* already on its way out through ipoe_dellink() or a flush */ + ret = 0; + goto out_unlock; + } + + ses->dying = 1; + if (ses->peer_addr) list_del_rcu(&ses->entry); list_del(&ses->entry2); @@ -1431,6 +1529,8 @@ static int ipoe_nl_cmd_flush(struct sk_buff *skb, struct genl_info *info) list_splice_init(&ipoe_list2, &list); list_for_each_entry(ses, &list, entry2) { + ses->dying = 1; + if (ses->peer_addr) list_del_rcu(&ses->entry); if (ses->u.hwaddr_u) @@ -2020,6 +2120,12 @@ static int __init ipoe_init(void) skb_queue_head_init(&ipoe_queue); INIT_WORK(&ipoe_queue_work, ipoe_process_queue); + err = rtnl_link_register(&ipoe_link_ops); + if (err < 0) { + printk(KERN_INFO "ipoe: can't register link operations\n"); + return err; + } + #if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) && RHEL_MAJOR < 7 err = genl_register_family_with_ops(&ipoe_nl_family, ipoe_nl_ops, ARRAY_SIZE(ipoe_nl_ops)); #elif LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0) @@ -2029,6 +2135,7 @@ static int __init ipoe_init(void) #endif if (err < 0) { printk(KERN_INFO "ipoe: can't register netlink interface\n"); + rtnl_link_unregister(&ipoe_link_ops); return err; } @@ -2037,6 +2144,7 @@ static int __init ipoe_init(void) if (err < 0) { printk(KERN_INFO "ipoe: can't register netlink multicast group\n"); genl_unregister_family(&ipoe_nl_family); + rtnl_link_unregister(&ipoe_link_ops); return err; } #endif @@ -2056,6 +2164,10 @@ static void __exit ipoe_fini(void) #endif genl_unregister_family(&ipoe_nl_family); + /* takes down whatever sessions are left through ipoe_dellink() and + * keeps rtnetlink from starting another one behind our back */ + rtnl_link_unregister(&ipoe_link_ops); + down(&ipoe_wlock); up(&ipoe_wlock); -- cgit v1.2.3 From 9015abafb60f950a6b1a800f84cbf569b07828a1 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 19:12:49 +0300 Subject: ipoe: include net/rtnetlink.h, insert modules in ci even after a failure rtnl_link_register() and struct rtnl_link_ops were reaching the driver through some other header rather than through net/rtnetlink.h, which is the kind of thing that only shows up when building against a different kernel. Include it directly. The workflows insert the kernel modules between two pytest runs, and the runs that follow are marked 'if: always()' while the insmod steps are not. A failure in an earlier, unrelated test therefore skips the insmod but still runs the tests that need the module, which then report a missing driver instead of the original problem. Mark the insmod steps 'if: always()' as well, so that the later runs test what they are supposed to. --- .github/workflows/run-tests.yml | 6 ++++++ drivers/ipoe/ipoe.c | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers/ipoe') diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9cd47a00..10f238cc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -145,6 +145,7 @@ jobs: sudo dmesg" - name: Insert ipoe kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && sudo insmod build/drivers/ipoe/driver/ipoe.ko && @@ -163,12 +164,14 @@ jobs: sudo dmesg" - name: Insert vlan_mon kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && sudo insmod build/drivers/vlan_mon/driver/vlan_mon.ko && lsmod | grep vlan_mon" - name: Insert ppposeq kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 user@localhost "cd accel-ppp && sudo modprobe pppox && sudo insmod build/drivers/ppposeq/driver/ppposeq.ko && @@ -290,6 +293,7 @@ jobs: doas dmesg" - name: Insert ipoe kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 alpine@localhost "cd accel-ppp && doas insmod build/drivers/ipoe/driver/ipoe.ko && @@ -308,12 +312,14 @@ jobs: doas dmesg" - name: Insert vlan_mon kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 alpine@localhost "cd accel-ppp && doas insmod build/drivers/vlan_mon/driver/vlan_mon.ko && lsmod | grep vlan_mon" - name: Insert ppposeq kernel module + if: ${{ always() }} run: > ssh -i ssh-key -p2222 alpine@localhost "cd accel-ppp && doas modprobe pppox && doas insmod build/drivers/ppposeq/driver/ppposeq.ko && diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index a9b11fa2..251fec96 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3