-
Notifications
You must be signed in to change notification settings - Fork 100
Dev Docs Components Appletalk
WIP - ALL LINKS IN THIS WIKI STRUCTURE ARE CURRENTLY BROKEN DURING WIKI MIGRATION
THESE ARE COMMUNITY DOCS
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 initialization -
etc/atalkd/config.c
- AppleTalk network configuration parsing and management -
etc/atalkd/route.c
- RTMP routing table management and updates -
include/atalk/ddp.h
- DDP protocol definitions and structures -
include/atalk/atp.h
- ATP transaction protocol definitions -
include/atalk/nbp.h
- NBP name binding protocol definitions
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
DDP provides the network layer functionality for AppleTalk networking.
Implementation Files:
-
libatalk/ddp/ddp.c
- DDP packet handling and socket operations -
include/atalk/ddp.h
- DDP protocol structures and constants -
etc/atalkd/ddp.c
- DDP daemon implementation and packet routing -
libatalk/ddp/ddp_addr.c
- AppleTalk address manipulation functions
// 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
};
// 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
};
RTMP manages routing between AppleTalk networks.
Implementation Files:
-
etc/atalkd/rtmp.c
- RTMP packet processing and route management -
etc/atalkd/route.c
- Routing table manipulation and maintenance -
include/atalk/rtmp.h
- RTMP protocol definitions and structures -
libatalk/rtmp/rtmp_send.c
- RTMP packet transmission functions
// 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;
}
NBP provides name-to-address resolution services.
Implementation Files:
-
etc/atalkd/nbp.c
- NBP name registration and lookup implementation -
include/atalk/nbp.h
- NBP protocol structures and message types -
libatalk/nbp/nbp_name.c
- NBP name parsing and validation functions -
libatalk/nbp/nbp_rgstr.c
- NBP registration and confirmation handling
// 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
};
// 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;
}
ZIP manages zone information and network-to-zone mappings.
Implementation Files:
-
etc/atalkd/zip.c
- ZIP protocol implementation and zone management -
include/atalk/zip.h
- ZIP protocol definitions and zone structures -
libatalk/zip/zip_getzonelist.c
- Zone list retrieval functions -
etc/atalkd/zones.c
- Zone table management and updates
// 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;
}
ATP provides reliable transaction services over DDP.
Implementation Files:
-
libatalk/atp/atp_packet.c
- ATP packet construction and parsing -
libatalk/atp/atp_rresp.c
- ATP response handling and retry logic -
include/atalk/atp.h
- ATP protocol definitions and transaction structures -
libatalk/atp/atp_sreq.c
- ATP request transmission and timeout 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);
}
Implementation Files:
-
etc/afpd/afp_asp.c
- AFP over ASP (AppleTalk Session Protocol) integration -
libatalk/asp/asp_getsess.c
- ASP session management functions -
include/atalk/asp.h
- ASP protocol definitions and session structures
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;
}
Support for AppleTalk printer sharing:
Implementation Files:
-
etc/papd/main.c
- PAP daemon main implementation -
etc/papd/print.c
- Print job handling and spooling -
libatalk/pap/pap_open.c
- PAP connection establishment -
include/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;
}
Implementation Files:
-
etc/atalkd/config.c
- AppleTalk network configuration parsing -
etc/atalkd/main.c
- Interface initialization and management -
etc/atalkd/interface.c
- Network interface handling and setup
// 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;
}
// 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;
}
Implementation Files:
-
sys/generic/sys_generic.c
- Generic Unix AppleTalk socket implementation -
sys/linux/ddp.c
- Linux-specific DDP socket operations -
sys/netbsd/ddp_netbsd.c
- NetBSD AppleTalk kernel interface -
include/atalk/paths.h
- Platform-specific path definitions
// 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-specific implementation
#ifdef __NetBSD__
static int netbsd_atalk_init(void) {
// NetBSD has built-in netatalk support in kernel
return 0;
}
#endif /* __NetBSD__ */
Implementation Files:
-
etc/atalkd/phase.c
- AppleTalk Phase 1/Phase 2 compatibility handling -
libatalk/compat/
- Compatibility functions for legacy protocol versions -
etc/atalkd/multicast.c
- EtherTalk multicast address management -
include/atalk/compat.h
- Legacy protocol compatibility definitions
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
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.
Resources
- Getting Started
- FAQ
- Troubleshooting
- Connect to AFP Server
- Webmin Module
- Benchmarks
- Interoperability with Samba
OS Specific Guides
- Installing Netatalk on Alpine Linux
- Installing Netatalk on Debian Linux
- Installing Netatalk on Fedora Linux
- Installing Netatalk on FreeBSD
- Installing Netatalk on macOS
- Installing Netatalk on NetBSD
- Installing Netatalk on OmniOS
- Installing Netatalk on OpenBSD
- Installing Netatalk on OpenIndiana
- Installing Netatalk on openSUSE
- Installing Netatalk on Solaris
- Installing Netatalk on Ubuntu
Tech Notes
- Kerberos
- Special Files and Folders
- Spotlight
- MySQL CNID Backend
- Slow AFP read performance
- Limiting Time Machine volumes
- Netatalk and ZFS nbmand property
Retro AFP
Development