Dev Docs AppleTalk Networking
- AppleTalk Networking (atalkd)
- Overview
- Architecture
- Core Components
- AppleTalk Service Integration
- Network Configuration and Management
- Platform-Specific Implementation
- Legacy Support and Compatibility
AppleTalk Networking (atalkd)
Overview
The atalkd daemon implements the AppleTalk protocol suite, enabling Netatalk to support legacy Mac systems, Apple IIs, and Lisa computers. This component provides the networking foundation for classic AFP over AppleTalk connections and additional services like printer sharing and time synchronization.
Implementation Files:
etc/atalkd/main.c- Main AppleTalk daemon entry point and initializationetc/atalkd/config.c- AppleTalk network configuration parsing and managementetc/atalkd/route.c- RTMP routing table management and updatesinclude/atalk/ddp.h- DDP protocol definitions and structuresinclude/atalk/atp.h- ATP transaction protocol definitionsinclude/atalk/nbp.h- NBP name binding protocol definitions
Architecture
AppleTalk Protocol Stack
graph TB
subgraph "Application Layer"
A[AFP - Apple Filing Protocol]
B[PAP - Printer Access Protocol]
C[ASP - AppleTalk Session Protocol]
end
subgraph "Transport Layer"
D[ATP - AppleTalk Transaction Protocol]
E[RTMP - Routing Table Maintenance]
F[AEP - AppleTalk Echo Protocol]
end
subgraph "Network Layer"
G[DDP - Datagram Delivery Protocol]
H[NBP - Name Binding Protocol]
I[ZIP - Zone Information Protocol]
end
subgraph "Data Link Layer"
J[EtherTalk - Ethernet]
K[LocalTalk - Serial]
L[TokenTalk - Token Ring]
end
A --> C
B --> D
C --> D
D --> G
E --> G
F --> G
G --> H
G --> I
H --> J
I --> J
J --> K
J --> L
Core Components
1. DDP (Datagram Delivery Protocol)
DDP provides the network layer functionality for AppleTalk networking.
Implementation Files:
libatalk/ddp/ddp.c- DDP packet handling and socket operationsinclude/atalk/ddp.h- DDP protocol structures and constantsetc/atalkd/ddp.c- DDP daemon implementation and packet routinglibatalk/ddp/ddp_addr.c- AppleTalk address manipulation functions
Network Address Structure
// AppleTalk network address
struct at_addr {
uint16_t s_net; // Network number (0-65534)
uint8_t s_node; // Node number (1-254)
};
// Socket address structure
struct sockaddr_at {
sa_family_t sat_family; // AF_APPLETALK
uint8_t sat_port; // Socket number
struct at_addr sat_addr; // Network address
char sat_zero[8]; // Padding
};
Network Configuration
// Interface configuration
struct interface {
char *i_name; // Interface name (e.g., "eth0")
int i_flags; // Interface flags
struct at_addr i_addr; // Our AppleTalk address
struct at_addr i_caddr; // Cable range start
struct at_addr i_daddr; // Cable range end
// Zone information
char *i_zone; // Default zone name
struct ziptab *i_zones; // Available zones
// Network parameters
int i_time; // Routing update timer
int i_acquire; // Address acquisition state
struct rtmptab *i_rt; // Routing table
struct interface *i_next; // Next interface
};
2. RTMP (Routing Table Maintenance Protocol)
RTMP manages routing between AppleTalk networks.
Implementation Files:
etc/atalkd/rtmp.c- RTMP packet processing and route managementetc/atalkd/route.c- Routing table manipulation and maintenanceinclude/atalk/rtmp.h- RTMP protocol definitions and structureslibatalk/rtmp/rtmp_send.c- RTMP packet transmission functions
Routing Table Management
// Routing table entry
struct rtmptab {
struct at_addr rt_firstnet; // First network in range
struct at_addr rt_lastnet; // Last network in range
struct at_addr rt_router; // Next hop router
int rt_flags; // Route flags
int rt_hops; // Hop count
int rt_time; // Age timer
struct interface *rt_iface; // Output interface
struct rtmptab *rt_next; // Next route
};
// Route flags
#define RTF_UP 0x01 // Route is up
#define RTF_HOST 0x02 // Host route
#define RTF_GATEWAY 0x04 // Route via gateway
#define RTF_STATIC 0x08 // Static route
// RTMP packet processing
static int rtmp_packet(struct interface *iface, char *packet, int len) {
struct rtmp_hdr *hdr = (struct rtmp_hdr *)packet;
struct rtmp_tuple *tuple;
int tuples, i;
// Validate packet
if (len < sizeof(struct rtmp_hdr)) {
return -1;
}
tuples = (len - sizeof(struct rtmp_hdr)) / sizeof(struct rtmp_tuple);
tuple = (struct rtmp_tuple *)(packet + sizeof(struct rtmp_hdr));
// Process routing updates
for (i = 0; i < tuples; i++, tuple++) {
if (ntohs(tuple->rt_net) != 0) {
rtmp_update_route(iface, ntohs(tuple->rt_net),
ntohs(tuple->rt_net), hdr->rtmp_id,
tuple->rt_hops & 0x7f);
}
}
return 0;
}
3. NBP (Name Binding Protocol)
NBP provides name-to-address resolution services.
Implementation Files:
etc/atalkd/nbp.c- NBP name registration and lookup implementationinclude/atalk/nbp.h- NBP protocol structures and message typeslibatalk/nbp/nbp_name.c- NBP name parsing and validation functionslibatalk/nbp/nbp_rgstr.c- NBP registration and confirmation handling
Name Tuple Structure
// NBP name tuple
struct nbptuple {
char object[32]; // Object name (e.g., "MyServer")
char type[32]; // Service type (e.g., "AFPServer")
char zone[32]; // Zone name (e.g., "Engineering")
};
// NBP registration entry
struct nbpentry {
struct nbptuple ne_name; // Service name
struct sockaddr_at ne_addr; // Service address
int ne_fd; // Associated socket
time_t ne_time; // Registration time
struct nbpentry *ne_next; // Next entry
};
Name Registration and Lookup
// Register a service name
int nbp_register(const char *object, const char *type, const char *zone,
struct sockaddr_at *addr) {
struct nbpentry *ne;
struct nbp_hdr hdr;
char packet[ATP_MAXDATA];
int len;
// Create registration entry
ne = malloc(sizeof(struct nbpentry));
if (ne == NULL) {
return -1;
}
strncpy(ne->ne_name.object, object, sizeof(ne->ne_name.object) - 1);
strncpy(ne->ne_name.type, type, sizeof(ne->ne_name.type) - 1);
strncpy(ne->ne_name.zone, zone, sizeof(ne->ne_name.zone) - 1);
ne->ne_addr = *addr;
ne->ne_time = time(NULL);
// Build NBP register packet
hdr.nh_op = NBP_REGISTER;
hdr.nh_cnt = 1;
hdr.nh_id = nbp_next_id++;
len = nbp_build_packet(&hdr, &ne->ne_name, &ne->ne_addr, packet);
// Send registration
if (nbp_send_packet(packet, len) == 0) {
// Add to local registration table
ne->ne_next = nbp_table;
nbp_table = ne;
return 0;
}
free(ne);
return -1;
}
// Look up service names
int nbp_lookup(const char *object, const char *type, const char *zone,
struct nbpentry **results, int max_results) {
struct nbp_hdr hdr;
struct nbptuple tuple;
char packet[ATP_MAXDATA];
int len, count = 0;
// Build lookup request
strncpy(tuple.object, object, sizeof(tuple.object) - 1);
strncpy(tuple.type, type, sizeof(tuple.type) - 1);
strncpy(tuple.zone, zone, sizeof(tuple.zone) - 1);
hdr.nh_op = NBP_LOOKUP;
hdr.nh_cnt = 1;
hdr.nh_id = nbp_next_id++;
len = nbp_build_packet(&hdr, &tuple, NULL, packet);
// Send lookup request and wait for responses
if (nbp_send_packet(packet, len) == 0) {
count = nbp_collect_responses(results, max_results, hdr.nh_id);
}
return count;
}
4. ZIP (Zone Information Protocol)
ZIP manages zone information and network-to-zone mappings.
Implementation Files:
etc/atalkd/zip.c- ZIP protocol implementation and zone managementinclude/atalk/zip.h- ZIP protocol definitions and zone structureslibatalk/zip/zip_getzonelist.c- Zone list retrieval functionsetc/atalkd/zones.c- Zone table management and updates
Zone Management
// Zone table entry
struct ziptab {
char zt_name[32]; // Zone name
struct at_addr zt_net; // Network number
int zt_flags; // Zone flags
time_t zt_time; // Last update time
struct ziptab *zt_next; // Next zone
};
// Get zone list for network
static int zip_getzonelist(struct interface *iface) {
struct zip_hdr hdr;
char packet[DDP_MAXDATA];
int len;
// Build zone list request
hdr.zh_op = ZIP_GETZONELIST;
hdr.zh_cnt = 0;
memcpy(packet, &hdr, sizeof(hdr));
len = sizeof(hdr);
// Send request to router
struct sockaddr_at router_addr;
router_addr.sat_family = AF_APPLETALK;
router_addr.sat_port = ZIP_SOCKET;
router_addr.sat_addr = iface->i_rt->rt_router;
if (sendto(zip_socket, packet, len, 0,
(struct sockaddr *)&router_addr, sizeof(router_addr)) < 0) {
perror("zip sendto");
return -1;
}
return 0;
}
// Process zone information reply
static int zip_reply(char *packet, int len, struct sockaddr_at *from) {
struct zip_hdr *hdr = (struct zip_hdr *)packet;
char *zone_data = packet + sizeof(struct zip_hdr);
int zones_count = hdr->zh_cnt;
int i, zone_len;
for (i = 0; i < zones_count && zone_data < packet + len; i++) {
zone_len = *zone_data++;
if (zone_len > 0 && zone_data + zone_len <= packet + len) {
// Add zone to table
zip_add_zone(zone_data, zone_len, from->sat_addr);
zone_data += zone_len;
}
}
return 0;
}
5. ATP (AppleTalk Transaction Protocol)
ATP provides reliable transaction services over DDP.
Implementation Files:
libatalk/atp/atp_packet.c- ATP packet construction and parsinglibatalk/atp/atp_rresp.c- ATP response handling and retry logicinclude/atalk/atp.h- ATP protocol definitions and transaction structureslibatalk/atp/atp_sreq.c- ATP request transmission and timeout management
Transaction Management
// ATP transaction control block
struct atp_tcb {
uint16_t at_tid; // Transaction ID
uint8_t at_bitmap; // Response bitmap
int at_socket; // Socket descriptor
struct sockaddr_at at_addr; // Peer address
// Timing and retry
int at_retry; // Retry count
int at_timeout; // Timeout value
time_t at_time; // Last send time
// Data buffers
char *at_req; // Request data
int at_reqlen; // Request length
char *at_resp[8]; // Response buffers
int at_resplen[8]; // Response lengths
struct atp_tcb *at_next; // Next TCB
};
// Send ATP request
int atp_sendreq(int socket, struct sockaddr_at *dest,
char *data, int len, uint8_t bitmap) {
struct atp_hdr hdr;
struct iovec iov[2];
struct msghdr msg;
// Build ATP header
hdr.ah_flags = ATP_TREQ;
hdr.ah_bitmap = bitmap;
hdr.ah_tid = htons(atp_next_tid++);
// Set up scatter-gather I/O
iov[0].iov_base = &hdr;
iov[0].iov_len = sizeof(hdr);
iov[1].iov_base = data;
iov[1].iov_len = len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = dest;
msg.msg_namelen = sizeof(struct sockaddr_at);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
return sendmsg(socket, &msg, 0);
}
AppleTalk Service Integration
Implementation Files:
etc/afpd/afp_asp.c- AFP over ASP (AppleTalk Session Protocol) integrationlibatalk/asp/asp_getsess.c- ASP session management functionsinclude/atalk/asp.h- ASP protocol definitions and session structures
1. AFP over AppleTalk
Integration with the AFP daemon for file sharing:
// ASP (AppleTalk Session Protocol) support
struct asp_session {
int as_socket; // ATP socket
struct sockaddr_at as_addr; // Client address
uint8_t as_session; // Session ID
uint8_t as_sequence; // Sequence number
int as_state; // Session state
// Buffers
char as_cmdbuf[ATP_MAXDATA];
char as_replybuf[ATP_MAXDATA];
int as_cmdlen;
int as_replylen;
};
// Handle AFP over ASP
static int asp_handle_request(struct asp_session *session) {
struct asp_hdr *hdr = (struct asp_hdr *)session->as_cmdbuf;
char *afp_data = session->as_cmdbuf + sizeof(struct asp_hdr);
int afp_len = session->as_cmdlen - sizeof(struct asp_hdr);
int reply_len;
// Validate ASP header
if (hdr->ash_flags != ASP_COMMAND) {
return -1;
}
// Process AFP command
reply_len = afp_handle_command(afp_data, afp_len,
session->as_replybuf + sizeof(struct asp_hdr),
ATP_MAXDATA - sizeof(struct asp_hdr));
// Build ASP reply
struct asp_hdr *reply_hdr = (struct asp_hdr *)session->as_replybuf;
reply_hdr->ash_flags = ASP_REPLY;
reply_hdr->ash_sequence = hdr->ash_sequence;
reply_hdr->ash_error = (reply_len < 0) ? ASP_ERR_SERVER : ASP_ERR_OK;
session->as_replylen = sizeof(struct asp_hdr) +
((reply_len > 0) ? reply_len : 0);
return 0;
}
2. Printer Access Protocol (PAP)
Support for AppleTalk printer sharing:
Implementation Files:
etc/papd/main.c- PAP daemon main implementationetc/papd/print.c- Print job handling and spoolinglibatalk/pap/pap_open.c- PAP connection establishmentinclude/atalk/pap.h- PAP protocol definitions and structures
// PAP connection state
struct pap_conn {
int pc_socket; // ATP socket
struct sockaddr_at pc_printer; // Printer address
uint8_t pc_connid; // Connection ID
int pc_state; // Connection state
// Print job data
char *pc_data; // Print data buffer
int pc_datalen; // Data length
int pc_offset; // Current offset
};
// Send data to AppleTalk printer
int pap_write(struct pap_conn *conn, const char *data, int len) {
struct pap_hdr hdr;
char packet[ATP_MAXDATA];
int packet_len, sent = 0;
while (sent < len) {
// Calculate packet size
packet_len = min(len - sent, ATP_MAXDATA - sizeof(struct pap_hdr));
// Build PAP header
hdr.ph_connid = conn->pc_connid;
hdr.ph_type = PAP_DATA;
hdr.ph_sequence = conn->pc_sequence++;
// Copy data
memcpy(packet, &hdr, sizeof(hdr));
memcpy(packet + sizeof(hdr), data + sent, packet_len);
// Send packet
if (atp_sendreq(conn->pc_socket, &conn->pc_printer,
packet, sizeof(hdr) + packet_len, 0x01) < 0) {
return -1;
}
sent += packet_len;
}
return sent;
}
Network Configuration and Management
Implementation Files:
etc/atalkd/config.c- AppleTalk network configuration parsingetc/atalkd/main.c- Interface initialization and managementetc/atalkd/interface.c- Network interface handling and setup
1. Interface Configuration
// Configure AppleTalk interface
static int configure_interface(const char *ifname, const char *zone) {
struct interface *iface;
struct ifreq ifr;
int sock;
// Allocate interface structure
iface = calloc(1, sizeof(struct interface));
if (iface == NULL) {
return -1;
}
iface->i_name = strdup(ifname);
iface->i_zone = strdup(zone);
// Get interface index and flags
sock = socket(AF_APPLETALK, SOCK_DGRAM, 0);
if (sock < 0) {
free(iface);
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
iface->i_flags = ifr.ifr_flags;
}
close(sock);
// Add to interface list
iface->i_next = interfaces;
interfaces = iface;
return 0;
}
2. Address Assignment
// Acquire AppleTalk address for interface
static int acquire_address(struct interface *iface) {
struct at_addr probe_addr;
int attempts = 0;
// Generate random address in valid range
do {
probe_addr.s_net = htons(random() % 65534 + 1);
probe_addr.s_node = random() % 254 + 1;
attempts++;
} while (attempts < 10 && address_in_use(&probe_addr));
if (attempts >= 10) {
LOG(log_error, "Failed to acquire AppleTalk address");
return -1;
}
// Verify address is not in use
if (aarp_probe(&probe_addr) == 0) {
iface->i_addr = probe_addr;
LOG(log_info, "Acquired AppleTalk address %u.%u",
ntohs(probe_addr.s_net), probe_addr.s_node);
return 0;
}
return -1;
}
Platform-Specific Implementation
Implementation Files:
sys/generic/sys_generic.c- Generic Unix AppleTalk socket implementationsys/linux/ddp.c- Linux-specific DDP socket operationssys/netbsd/ddp_netbsd.c- NetBSD AppleTalk kernel interfaceinclude/atalk/paths.h- Platform-specific path definitions
Linux AppleTalk Kernel Module
// Linux-specific AppleTalk socket operations
#ifdef __linux__
static int linux_atalk_socket(void) {
int sock;
struct sockaddr_at addr;
// Create AppleTalk socket
sock = socket(AF_APPLETALK, SOCK_DGRAM, 0);
if (sock < 0) {
if (errno == EPROTONOSUPPORT) {
LOG(log_error, "AppleTalk protocol not supported. "
"Load the appletalk kernel module.");
}
return -1;
}
// Bind to DDP socket
memset(&addr, 0, sizeof(addr));
addr.sat_family = AF_APPLETALK;
addr.sat_port = ATADDR_ANYPORT;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(sock);
return -1;
}
return sock;
}
#endif /* __linux__ */
NetBSD AppleTalk Support
// NetBSD-specific implementation
#ifdef __NetBSD__
static int netbsd_atalk_init(void) {
// NetBSD has built-in netatalk support in kernel
return 0;
}
#endif /* __NetBSD__ */
Legacy Support and Compatibility
Implementation Files:
etc/atalkd/phase.c- AppleTalk Phase 1/Phase 2 compatibility handlinglibatalk/compat/- Compatibility functions for legacy protocol versionsetc/atalkd/multicast.c- EtherTalk multicast address managementinclude/atalk/compat.h- Legacy protocol compatibility definitions
1. Protocol Version Compatibility
AppleTalk support maintains compatibility with various protocol versions:
- Phase 1: Original LocalTalk protocol (single network)
- Phase 2: Extended AppleTalk (multiple networks, zones)
- EtherTalk: AppleTalk over Ethernet
- TokenTalk: AppleTalk over Token Ring
2. Service Discovery
Classic Mac systems rely on NBP for service discovery:
// Register common services
static void register_services(void) {
struct sockaddr_at addr;
// Get our address
get_local_address(&addr);
// Register AFP server
nbp_register("Netatalk Server", "AFPServer", "*", &addr);
// Register printer services if enabled
if (papd_enabled) {
nbp_register("Netatalk Printer", "LaserWriter", "*", &addr);
}
// Register time service if enabled
if (timelord_enabled) {
nbp_register("Netatalk Time", "TimeLord", "*", &addr);
}
}
The AppleTalk implementation in Netatalk provides comprehensive support for legacy Mac networking, enabling modern Unix systems to seamlessly integrate with classic Mac environments while maintaining the authentic AppleTalk networking experience that these systems expect.
Footnotes
This is a mirror of the Netatalk GitHub Wiki
Last updated 2025-12-27