1/* $NetBSD: af_atalk.c,v 1.17 2010/12/13 17:35:08 pooka Exp $ */ 2 3/* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__RCSID("$NetBSD: af_atalk.c,v 1.17 2010/12/13 17:35:08 pooka Exp $"); 35#endif /* not lint */ 36 37#include <sys/param.h> 38#include <sys/ioctl.h> 39#include <sys/socket.h> 40 41#include <net/if.h> 42 43#include <netatalk/at.h> 44 45#include <netdb.h> 46 47#include <err.h> 48#include <errno.h> 49#include <string.h> 50#include <stddef.h> 51#include <stdlib.h> 52#include <stdio.h> 53#include <util.h> 54 55#include "env.h" 56#include "af_inetany.h" 57#include "parse.h" 58#include "extern.h" 59#include "prog_ops.h" 60 61#ifndef satocsat 62#define satocsat(__sa) ((const struct sockaddr_at *)(__sa)) 63#endif 64 65static void at_status(prop_dictionary_t, prop_dictionary_t, bool); 66static void at_commit_address(prop_dictionary_t, prop_dictionary_t); 67 68static void at_constructor(void) __attribute__((constructor)); 69 70static struct afswtch ataf = { 71 .af_name = "atalk", .af_af = AF_APPLETALK, .af_status = at_status, 72 .af_addr_commit = at_commit_address 73}; 74struct pinteger phase = PINTEGER_INITIALIZER1(&phase, "phase", 75 1, 2, 10, NULL, "phase", &command_root.pb_parser); 76 77struct pstr parse_range = PSTR_INITIALIZER(&range, "range", NULL, "range", 78 &command_root.pb_parser); 79 80static const struct kwinst atalkkw[] = { 81 {.k_word = "phase", .k_nextparser = &phase.pi_parser} 82 , {.k_word = "range", .k_nextparser = &parse_range.ps_parser} 83}; 84 85struct pkw atalk = PKW_INITIALIZER(&atalk, "AppleTalk", NULL, NULL, 86 atalkkw, __arraycount(atalkkw), NULL); 87 88static cmdloop_branch_t branch; 89 90static void 91setatrange_impl(prop_dictionary_t env, prop_dictionary_t oenv, 92 struct netrange *nr) 93{ 94 char range[24]; 95 u_short first = 123, last = 123; 96 97 if (getargstr(env, "range", range, sizeof(range)) == -1) 98 return; 99 100 if (sscanf(range, "%hu-%hu", &first, &last) != 2 || 101 first == 0 || last == 0 || first > last) 102 errx(EXIT_FAILURE, "%s: illegal net range: %u-%u", range, 103 first, last); 104 nr->nr_firstnet = htons(first); 105 nr->nr_lastnet = htons(last); 106} 107 108static void 109at_commit_address(prop_dictionary_t env, prop_dictionary_t oenv) 110{ 111 struct ifreq ifr; 112 struct ifaliasreq ifra __attribute__((aligned(4))); 113 struct afparam atparam = { 114 .req = BUFPARAM(ifra) 115 , .dgreq = BUFPARAM(ifr) 116 , .name = { 117 {.buf = ifr.ifr_name, 118 .buflen = sizeof(ifr.ifr_name)} 119 , {.buf = ifra.ifra_name, 120 .buflen = sizeof(ifra.ifra_name)} 121 } 122 , .dgaddr = BUFPARAM(ifr.ifr_addr) 123 , .addr = BUFPARAM(ifra.ifra_addr) 124 , .dst = BUFPARAM(ifra.ifra_dstaddr) 125 , .brd = BUFPARAM(ifra.ifra_broadaddr) 126 , .mask = BUFPARAM(ifra.ifra_mask) 127 , .aifaddr = IFADDR_PARAM(SIOCAIFADDR) 128 , .difaddr = IFADDR_PARAM(SIOCDIFADDR) 129 , .gifaddr = IFADDR_PARAM(SIOCGIFADDR) 130 , .defmask = {.buf = NULL, .buflen = 0} 131 }; 132 struct netrange nr = {.nr_phase = 2}; /* AppleTalk net range */ 133 prop_data_t d, d0; 134 prop_dictionary_t ienv; 135 struct paddr_prefix *addr; 136 struct sockaddr_at *sat; 137 138 if ((d0 = (prop_data_t)prop_dictionary_get(env, "address")) == NULL) 139 return; 140 141 addr = prop_data_data(d0); 142 143 sat = (struct sockaddr_at *)&addr->pfx_addr; 144 145 (void)prop_dictionary_get_uint8(env, "phase", &nr.nr_phase); 146 /* Default range of one */ 147 nr.nr_firstnet = nr.nr_lastnet = sat->sat_addr.s_net; 148 setatrange_impl(env, oenv, &nr); 149 150 if (ntohs(nr.nr_firstnet) > ntohs(sat->sat_addr.s_net) || 151 ntohs(nr.nr_lastnet) < ntohs(sat->sat_addr.s_net)) 152 errx(EXIT_FAILURE, "AppleTalk address is not in range"); 153 memcpy(&sat->sat_zero, &nr, sizeof(nr)); 154 155 /* Copy the new address to a temporary input environment */ 156 157 d = prop_data_create_data_nocopy(addr, paddr_prefix_size(addr)); 158 ienv = prop_dictionary_copy_mutable(env); 159 160 if (d == NULL) 161 err(EXIT_FAILURE, "%s: prop_data_create_data", __func__); 162 if (ienv == NULL) 163 err(EXIT_FAILURE, "%s: prop_dictionary_copy_mutable", __func__); 164 165 if (!prop_dictionary_set(ienv, "address", (prop_object_t)d)) 166 err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__); 167 168 /* copy to output environment for good measure */ 169 if (!prop_dictionary_set(oenv, "address", (prop_object_t)d)) 170 err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__); 171 172 prop_object_release((prop_object_t)d); 173 174 memset(&ifr, 0, sizeof(ifr)); 175 memset(&ifra, 0, sizeof(ifra)); 176 commit_address(ienv, oenv, &atparam); 177 178 /* release temporary input environment */ 179 prop_object_release((prop_object_t)ienv); 180} 181 182static void 183sat_print1(const char *prefix, const struct sockaddr *sa) 184{ 185 char buf[40]; 186 int rc; 187 188 rc = getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, 0); 189 190 printf("%s%s", prefix, buf); 191} 192 193static void 194at_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force) 195{ 196 struct sockaddr_at *sat; 197 struct ifreq ifr; 198 int s; 199 const char *ifname; 200 unsigned short flags; 201 202 if ((s = getsock(AF_APPLETALK)) == -1) { 203 if (errno == EAFNOSUPPORT) 204 return; 205 err(EXIT_FAILURE, "getsock"); 206 } 207 if ((ifname = getifinfo(env, oenv, &flags)) == NULL) 208 err(EXIT_FAILURE, "%s: getifinfo", __func__); 209 210 memset(&ifr, 0, sizeof(ifr)); 211 estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 212 ifr.ifr_addr.sa_family = AF_APPLETALK; 213 if (prog_ioctl(s, SIOCGIFADDR, &ifr) != -1) 214 ; 215 else if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { 216 if (!force) 217 return; 218 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); 219 } else 220 warn("SIOCGIFADDR"); 221 sat = (struct sockaddr_at *)&ifr.ifr_addr; 222 223 sat_print1("\tatalk ", &ifr.ifr_addr); 224 225 if (flags & IFF_POINTOPOINT) { 226 estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 227 if (prog_ioctl(s, SIOCGIFDSTADDR, &ifr) == -1) { 228 if (errno == EADDRNOTAVAIL) 229 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); 230 else 231 warn("SIOCGIFDSTADDR"); 232 } 233 sat_print1(" --> ", &ifr.ifr_dstaddr); 234 } 235 if (flags & IFF_BROADCAST) { 236 /* note RTAX_BRD overlap with IFF_POINTOPOINT */ 237 /* note Appletalk broadcast is fixed. */ 238 printf(" broadcast %u.%u", ntohs(sat->sat_addr.s_net), 239 ATADDR_BCAST); 240 } 241 printf("\n"); 242} 243 244static void 245at_constructor(void) 246{ 247 register_family(&ataf); 248 cmdloop_branch_init(&branch, &atalk.pk_parser); 249 register_cmdloop_branch(&branch); 250} 251