The appletalk
Linux module provides support for AppleTalk sockets and
routing, allowing the Netatalk tools to work properly. There are, however, some
issues with the current implementation.
Note: All issues are now fixed; this document is left as reference.
Part of the appletalk
module initialization code was refactored during
development of the 5.1 version of the kernel. Unfortunately, a return
statement was missed in a function, resulting in the function always falling.
If you have atalkd
complaining that it can’t open an AppleTalk socket because
the protocol is not supported, try loading the appletalk
module manually,
by running modprobe appletalk
. If you get the following error:
modprobe: ERROR: could not insert 'appletalk': Cannot allocate memory
it means your kernel is affected. To fix it, you need to apply the following patch:
Fix atalk_proc_init success code path
--- linux/net/appletalk/atalk_proc.c 2020-06-01 01:49:15.000000000 +0200
+++ linux/net/appletalk/atalk_proc.c 2020-06-05 07:44:42.748258347 +0200
@@ -229,6 +229,8 @@
sizeof(struct aarp_iter_state), NULL))
goto out;
+ return 0;
+
out:
remove_proc_subtree("atalk", init_net.proc_net);
return -ENOMEM;
(Note that you normally don’t need to load the module before starting atalkd
;
it will be automatically loaded once an AppleTalk socket is opened)
This issue only affects the use of atalkd
in multi-interface configuration
(i.e. you have two Ethernet interfaces on the machine running atalkd
, and
both of them are connected to Mac computers using AppleTalk). In this
configuration, atalkd
is supposed to announce the network settings to the
computers so they get correct AppleTalk addresses assigned, and then the kernel
will route AppleTalk packets between interfaces so Macs on the first network
can communicate with Macs on the second network.
When AppleTalk is enabled on a Mac, it first tries to discover the network
configuration by sending a network information request packet on the AppleTalk
broadcast address (0.255
), port 6; the local AppleTalk router is supposed to
send back a packet indicating which AppleTalk network number(s) is/are
associated to this network. The Mac will then choose a random address in the
network, check that it does not conflict with another machine, and assign it
to itself.
When started, atalkd
configures the AppleTalk addresses on the machine, then
waits for network information requests to be received. Suppose that you have
the following AppleTalk configuration:
Interface | AppleTalk network number | Local AppleTalk address |
---|---|---|
eth1 |
1 |
1.1 |
eth2 |
2 |
2.1 |
In this configuration, after setting up the interfaces, atalkd
will create
two sockets, listening respectively on 1.1
port 6, and 2.1
port 6. When
receiving a network information request on the 1.1
socket, it will respond
“This is network 1” on the same socket; same thing for socket 2.
The problem is that Linux does not handle AppleTalk broadcast packets the
way atalkd
expects. When a broadcast packet is received on port 6, Linux
will look at any AppleTalk socket listening on port 6, and give the packet to
the first one it finds. (In practice, the packet will be given to the
most-recently created socket listening on the correct port).
In practice, that means that atalkd
will see all network information requests
come from the same socket, and will send replies on this socket, so they will
all come out on the same interface, which is not necessarily the one which
received the packet initially.
To fix this issue, the following patch modifies the socket selection process that is used when a broadcast packet is received; it will prioritize sockets listening on the address associated with the interface that received the packet (and has the right port). If no such socket is found, another random socket (listening on the right port) will be used, as before.
Prioritize sockets listening on the interface when receiving broadcast packets.
--- linux-5.7.0.orig/net/appletalk/ddp.c 2020-06-01 01:49:15.000000000 +0200
+++ linux-5.7.0/net/appletalk/ddp.c 2020-06-05 07:44:17.363132984 +0200
@@ -88,6 +88,7 @@
struct atalk_iface *atif)
{
struct sock *s;
+ struct sock *default_socket = NULL;
read_lock_bh(&atalk_sockets_lock);
sk_for_each(s, &atalk_sockets) {
@@ -96,9 +97,20 @@
if (to->sat_port != at->src_port)
continue;
+ /* Prioritize reception of broadcast packets on the socket whose
+ * address matches the interface's address */
if (to->sat_addr.s_net == ATADDR_ANYNET &&
- to->sat_addr.s_node == ATADDR_BCAST)
- goto found;
+ to->sat_addr.s_node == ATADDR_BCAST) {
+ /* Any socket with the right port can receive broadcast
+ * packets... */
+ default_socket = s;
+
+ /* ... but the socket whose address matches the interface's
+ * address (if any) is preferred */
+ if (atif->address.s_node == at->src_node &&
+ atif->address.s_net == at->src_net)
+ goto found;
+ }
if (to->sat_addr.s_net == at->src_net &&
(to->sat_addr.s_node == at->src_node ||
@@ -115,7 +127,8 @@
goto found;
}
}
- s = NULL;
+
+ s = default_socket;
found:
read_unlock_bh(&atalk_sockets_lock);
return s;
With this patch, you should be able to configure atalkd
to use multiple
interfaces and be able to route packets between these interfaces. Here is
a basic atalkd.conf
that you can use as long as no more than 252 Macs are
attached to each interface (which is likely):
some-interface -router -phase 2 -net 1 -addr 1.1 -zone "My Net"
some-other-iface -router -phase 2 -net 2 -addr 2.1 -zone "My Net"
etc...
Once atalkd
is started, any Mac on the network should join the right network
when AppleTalk is activated. The zone name My Net
should be displayed in
the AppleTalk or Network control panel, and the AppleTalk address should match
the network (you can see advanced information in the AppleTalk control panel
by switching to Advanced mode in the Edit menu)
If you want to patch the AppleTalk module on your Linux system, you can do it without having to rebuild the whole kernel as long as your distribution enables appletalk as a module (i.e has CONFIG_ATALK=m
in its kernel configuration); fortunately, this is the most common case.
The first step is to download the appletalk
module source:
curl https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz | \
tar -xJ --strip-components=2 linux-6.1/net/appletalk/
(You should ideally use a version of the AppleTalk module that matches the one from your distribution, but since the module has not changed since 2020 you are probably fine)
Go into the appletalk
module folder, then patch ddp.c
as indicated above.
Then: (you will need to do the following each time your kernel is updated):
- Run
make -C /lib/modules/$(uname -r)/build M=$PWD clean appletalk.ko
- Load the current AppleTalk module, then unload it to make sure its dependencies are loaded:
sudo modprobe appletalk && sudo rmmod appletalk
(If the module fails to unload, stopatalkd
and try again) - Load your patched module:
sudo insmod appletalk.ko
- Make sure everything works
- To make the change permanent (until the next kernel update), replace the file
/lib/modules/$(uname -r)/kernel/net/appletalk/appletalk.ko
with your patched module.
- 2024-04-27: Updated the module loading section to indicate which version fixed the issue, and hide it by default. Added some info on how to patch a system.
- 2024-07-30: Indicate that all issues are now fixed.
Further testing shows that this is possibly a bug in Netatalk's NBP Reply function. Not entirely out of the question that fixing the kernel stack exposed an old bug.