Skip to content

Dev Docs Components Appletalk

Andy Lemin edited this page Aug 16, 2025 · 2 revisions

WIP - ALL LINKS IN THIS WIKI STRUCTURE ARE CURRENTLY BROKEN DURING WIKI MIGRATION

THESE ARE COMMUNITY DOCS

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 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

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
Loading

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 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

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 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 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 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

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 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 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 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

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) integration
  • libatalk/asp/asp_getsess.c - ASP session management functions
  • include/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 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;
}

Network Configuration and Management

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

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 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 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 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

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.

Clone this wiki locally