udp.c revision 71006
147061Sbrian/*- 247061Sbrian * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org> 347061Sbrian * All rights reserved. 447061Sbrian * 547061Sbrian * Redistribution and use in source and binary forms, with or without 647061Sbrian * modification, are permitted provided that the following conditions 747061Sbrian * are met: 847061Sbrian * 1. Redistributions of source code must retain the above copyright 947061Sbrian * notice, this list of conditions and the following disclaimer. 1047061Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1147061Sbrian * notice, this list of conditions and the following disclaimer in the 1247061Sbrian * documentation and/or other materials provided with the distribution. 1347061Sbrian * 1447061Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1547061Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1647061Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1747061Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1847061Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1947061Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2047061Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2147061Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2247061Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2347061Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2447061Sbrian * SUCH DAMAGE. 2547061Sbrian * 2650479Speter * $FreeBSD: head/usr.sbin/ppp/udp.c 71006 2001-01-14 00:54:48Z brian $ 2747061Sbrian */ 2847061Sbrian 2947061Sbrian#include <sys/types.h> 3047061Sbrian#include <sys/socket.h> 3147061Sbrian#include <netinet/in.h> 3247061Sbrian#include <arpa/inet.h> 3347061Sbrian#include <netdb.h> 3447061Sbrian 3547061Sbrian#include <errno.h> 3647061Sbrian#include <stdio.h> 3747061Sbrian#include <stdlib.h> 3847061Sbrian#include <string.h> 3947769Sbrian#include <sysexits.h> 4071006Sbrian#include <sys/stat.h> 4147061Sbrian#include <sys/uio.h> 4247061Sbrian#include <termios.h> 4347061Sbrian#include <unistd.h> 4447061Sbrian 4547061Sbrian#include "layer.h" 4647061Sbrian#include "defs.h" 4747061Sbrian#include "mbuf.h" 4847061Sbrian#include "log.h" 4947061Sbrian#include "timer.h" 5047061Sbrian#include "lqr.h" 5147061Sbrian#include "hdlc.h" 5247061Sbrian#include "throughput.h" 5347061Sbrian#include "fsm.h" 5447061Sbrian#include "lcp.h" 5547061Sbrian#include "ccp.h" 5647061Sbrian#include "link.h" 5747061Sbrian#include "async.h" 5847061Sbrian#include "descriptor.h" 5947061Sbrian#include "physical.h" 6047769Sbrian#include "main.h" 6147061Sbrian#include "udp.h" 6247061Sbrian 6368424Sbrian 6468424Sbrian#define UDP_CONNECTED 1 6568424Sbrian#define UDP_UNCONNECTED 2 6668424Sbrian#define UDP_MAYBEUNCONNECTED 3 6768424Sbrian 6847061Sbrianstruct udpdevice { 6947061Sbrian struct device dev; /* What struct physical knows about */ 7047061Sbrian struct sockaddr_in sock; /* peer address */ 7168424Sbrian unsigned connected : 2; /* Have we connect()d ? */ 7247061Sbrian}; 7347061Sbrian 7447061Sbrian#define device2udp(d) ((d)->type == UDP_DEVICE ? (struct udpdevice *)d : NULL) 7547061Sbrian 7647769Sbrianint 7747769Sbrianudp_DeviceSize(void) 7847769Sbrian{ 7947769Sbrian return sizeof(struct udpdevice); 8047769Sbrian} 8147769Sbrian 8247061Sbrianstatic ssize_t 8347061Sbrianudp_Sendto(struct physical *p, const void *v, size_t n) 8447061Sbrian{ 8547061Sbrian struct udpdevice *dev = device2udp(p->handler); 8668424Sbrian int ret; 8747061Sbrian 8868424Sbrian switch (dev->connected) { 8968424Sbrian case UDP_CONNECTED: 9068424Sbrian ret = write(p->fd, v, n); 9168424Sbrian break; 9247061Sbrian 9368424Sbrian case UDP_UNCONNECTED: 9468424Sbrian default: 9568424Sbrian ret = sendto(p->fd, v, n, 0, (struct sockaddr *)&dev->sock, 9668424Sbrian sizeof dev->sock); 9768424Sbrian break; 9868424Sbrian } 9968424Sbrian if (dev->connected == UDP_MAYBEUNCONNECTED) { 10068424Sbrian if (ret == -1 && errno == EISCONN) { 10168424Sbrian dev->connected = UDP_CONNECTED; 10268424Sbrian ret = write(p->fd, v, n); 10368424Sbrian } else 10468424Sbrian dev->connected = UDP_UNCONNECTED; 10568424Sbrian } 10668424Sbrian 10768424Sbrian return ret; 10847061Sbrian} 10947061Sbrian 11047061Sbrianstatic ssize_t 11147061Sbrianudp_Recvfrom(struct physical *p, void *v, size_t n) 11247061Sbrian{ 11347061Sbrian struct udpdevice *dev = device2udp(p->handler); 11447061Sbrian int sz, ret; 11547061Sbrian 11668424Sbrian if (dev->connected == UDP_CONNECTED) 11747061Sbrian return read(p->fd, v, n); 11847061Sbrian 11947061Sbrian sz = sizeof dev->sock; 12047061Sbrian ret = recvfrom(p->fd, v, n, 0, (struct sockaddr *)&dev->sock, &sz); 12147061Sbrian 12247061Sbrian if (*p->name.full == '\0') { 12347061Sbrian snprintf(p->name.full, sizeof p->name.full, "%s:%d/udp", 12447061Sbrian inet_ntoa(dev->sock.sin_addr), ntohs(dev->sock.sin_port)); 12547061Sbrian p->name.base = p->name.full; 12647061Sbrian } 12747061Sbrian 12847061Sbrian return ret; 12947061Sbrian} 13047061Sbrian 13147061Sbrianstatic void 13247061Sbrianudp_Free(struct physical *p) 13347061Sbrian{ 13447061Sbrian struct udpdevice *dev = device2udp(p->handler); 13547061Sbrian 13647061Sbrian free(dev); 13747061Sbrian} 13847061Sbrian 13947061Sbrianstatic void 14047769Sbrianudp_device2iov(struct device *d, struct iovec *iov, int *niov, 14153684Sbrian int maxiov, int *auxfd, int *nauxfd) 14247061Sbrian{ 14347769Sbrian int sz = physical_MaxDeviceSize(); 14447769Sbrian 14547769Sbrian iov[*niov].iov_base = realloc(d, sz); 14647769Sbrian if (iov[*niov].iov_base == NULL) { 14747769Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); 14847769Sbrian AbortProgram(EX_OSERR); 14947769Sbrian } 15047769Sbrian iov[*niov].iov_len = sz; 15147061Sbrian (*niov)++; 15247061Sbrian} 15347061Sbrian 15447061Sbrianstatic const struct device baseudpdevice = { 15547061Sbrian UDP_DEVICE, 15647061Sbrian "udp", 15753733Sbrian { CD_NOTREQUIRED, 0 }, 15847061Sbrian NULL, 15947061Sbrian NULL, 16047061Sbrian NULL, 16147061Sbrian NULL, 16249472Sbrian NULL, 16352942Sbrian NULL, 16447061Sbrian udp_Free, 16547061Sbrian udp_Recvfrom, 16647061Sbrian udp_Sendto, 16747061Sbrian udp_device2iov, 16847061Sbrian NULL, 16947061Sbrian NULL 17047061Sbrian}; 17147061Sbrian 17247061Sbrianstruct device * 17347061Sbrianudp_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, 17452942Sbrian int maxiov, int *auxfd, int *nauxfd) 17547061Sbrian{ 17647061Sbrian if (type == UDP_DEVICE) { 17747461Sbrian struct udpdevice *dev = (struct udpdevice *)iov[(*niov)++].iov_base; 17847061Sbrian 17947769Sbrian dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 18047769Sbrian if (dev == NULL) { 18147769Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", 18247769Sbrian (int)(sizeof *dev)); 18347769Sbrian AbortProgram(EX_OSERR); 18447769Sbrian } 18547769Sbrian 18647061Sbrian /* Refresh function pointers etc */ 18747461Sbrian memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); 18847061Sbrian 18947461Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); 19047461Sbrian return &dev->dev; 19147461Sbrian } 19247061Sbrian 19347461Sbrian return NULL; 19447061Sbrian} 19547061Sbrian 19647061Sbrianstatic struct udpdevice * 19747061Sbrianudp_CreateDevice(struct physical *p, char *host, char *port) 19847061Sbrian{ 19947061Sbrian struct udpdevice *dev; 20047061Sbrian struct servent *sp; 20147061Sbrian 20247061Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 20347061Sbrian log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n", 20447061Sbrian p->link.name, strerror(errno)); 20547061Sbrian return NULL; 20647061Sbrian } 20747061Sbrian 20847061Sbrian dev->sock.sin_family = AF_INET; 20947061Sbrian dev->sock.sin_addr.s_addr = inet_addr(host); 21047061Sbrian dev->sock.sin_addr = GetIpAddr(host); 21147061Sbrian if (dev->sock.sin_addr.s_addr == INADDR_NONE) { 21247061Sbrian log_Printf(LogWARN, "%s: %s: unknown host\n", p->link.name, host); 21347061Sbrian free(dev); 21447061Sbrian return NULL; 21547061Sbrian } 21647061Sbrian dev->sock.sin_port = htons(atoi(port)); 21747061Sbrian if (dev->sock.sin_port == 0) { 21847061Sbrian sp = getservbyname(port, "udp"); 21947061Sbrian if (sp) 22047061Sbrian dev->sock.sin_port = sp->s_port; 22147061Sbrian else { 22247061Sbrian log_Printf(LogWARN, "%s: %s: unknown service\n", p->link.name, port); 22347061Sbrian free(dev); 22447061Sbrian return NULL; 22547061Sbrian } 22647061Sbrian } 22747061Sbrian 22847061Sbrian log_Printf(LogPHASE, "%s: Connecting to %s:%s/udp\n", p->link.name, 22947061Sbrian host, port); 23047061Sbrian 23147061Sbrian p->fd = socket(PF_INET, SOCK_DGRAM, 0); 23247061Sbrian if (p->fd >= 0) { 23347061Sbrian log_Printf(LogDEBUG, "%s: Opened udp socket %s\n", p->link.name, 23447061Sbrian p->name.full); 23547061Sbrian if (connect(p->fd, (struct sockaddr *)&dev->sock, sizeof dev->sock) == 0) { 23668424Sbrian dev->connected = UDP_CONNECTED; 23747061Sbrian return dev; 23847061Sbrian } else 23947061Sbrian log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno)); 24047061Sbrian } else 24147061Sbrian log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno)); 24247061Sbrian 24347061Sbrian close(p->fd); 24447061Sbrian p->fd = -1; 24547061Sbrian free(dev); 24647061Sbrian 24747061Sbrian return NULL; 24847061Sbrian} 24947061Sbrian 25047061Sbrianstruct device * 25147061Sbrianudp_Create(struct physical *p) 25247061Sbrian{ 25347061Sbrian char *cp, *host, *port, *svc; 25447061Sbrian struct udpdevice *dev; 25547061Sbrian 25647061Sbrian dev = NULL; 25747061Sbrian if (p->fd < 0) { 25852942Sbrian if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) { 25947061Sbrian *cp = '\0'; 26047061Sbrian host = p->name.full; 26147061Sbrian port = cp + 1; 26247061Sbrian svc = strchr(port, '/'); 26347061Sbrian if (svc && strcasecmp(svc, "/udp")) { 26447061Sbrian *cp = ':'; 26547061Sbrian return NULL; 26647061Sbrian } 26752942Sbrian if (svc) { 26852942Sbrian p->fd--; /* We own the device but maybe can't use it - change fd */ 26947061Sbrian *svc = '\0'; 27052942Sbrian } 27147061Sbrian 27247061Sbrian if (*host && *port) 27347061Sbrian dev = udp_CreateDevice(p, host, port); 27447061Sbrian 27547061Sbrian *cp = ':'; 27647061Sbrian if (svc) 27747061Sbrian *svc = '/'; 27847061Sbrian } 27947061Sbrian } else { 28047061Sbrian /* See if we're a connected udp socket */ 28171006Sbrian struct stat st; 28247061Sbrian 28371006Sbrian if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) { 28471006Sbrian int type, sz; 28571006Sbrian 28671006Sbrian sz = sizeof type; 28771006Sbrian if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) { 28871006Sbrian log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name); 28971006Sbrian close(p->fd); 29071006Sbrian p->fd = -1; 29147061Sbrian return NULL; 29247061Sbrian } 29347061Sbrian 29471006Sbrian if (sz == sizeof type && type == SOCK_DGRAM) { 29571006Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 29671006Sbrian log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n", 29771006Sbrian p->link.name, strerror(errno)); 29871006Sbrian return NULL; 29971006Sbrian } 30047061Sbrian 30171006Sbrian /* We can't getpeername().... */ 30271006Sbrian dev->connected = UDP_MAYBEUNCONNECTED; 30347061Sbrian 30471006Sbrian log_Printf(LogPHASE, "%s: Link is a udp socket\n", p->link.name); 30571006Sbrian 30671006Sbrian if (p->link.lcp.cfg.openmode != OPEN_PASSIVE) { 30771006Sbrian log_Printf(LogPHASE, "%s: Changing openmode to PASSIVE\n", 30871006Sbrian p->link.name); 30971006Sbrian p->link.lcp.cfg.openmode = OPEN_PASSIVE; 31071006Sbrian } 31147061Sbrian } 31247061Sbrian } 31347061Sbrian } 31447061Sbrian 31547061Sbrian if (dev) { 31647061Sbrian memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); 31747461Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); 31853733Sbrian if (p->cfg.cd.necessity != CD_DEFAULT) 31953733Sbrian log_Printf(LogWARN, "Carrier settings ignored\n"); 32047061Sbrian return &dev->dev; 32147061Sbrian } 32247061Sbrian 32347061Sbrian return NULL; 32447061Sbrian} 325