udp.c revision 89422
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 89422 2002-01-16 14:03:52Z 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", 15778410Sbrian 0, 15853733Sbrian { CD_NOTREQUIRED, 0 }, 15947061Sbrian NULL, 16047061Sbrian NULL, 16147061Sbrian NULL, 16247061Sbrian NULL, 16349472Sbrian NULL, 16452942Sbrian NULL, 16547061Sbrian udp_Free, 16647061Sbrian udp_Recvfrom, 16747061Sbrian udp_Sendto, 16847061Sbrian udp_device2iov, 16947061Sbrian NULL, 17047061Sbrian NULL 17147061Sbrian}; 17247061Sbrian 17347061Sbrianstruct device * 17447061Sbrianudp_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, 17552942Sbrian int maxiov, int *auxfd, int *nauxfd) 17647061Sbrian{ 17747061Sbrian if (type == UDP_DEVICE) { 17847461Sbrian struct udpdevice *dev = (struct udpdevice *)iov[(*niov)++].iov_base; 17947061Sbrian 18047769Sbrian dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 18147769Sbrian if (dev == NULL) { 18247769Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", 18347769Sbrian (int)(sizeof *dev)); 18447769Sbrian AbortProgram(EX_OSERR); 18547769Sbrian } 18647769Sbrian 18747061Sbrian /* Refresh function pointers etc */ 18847461Sbrian memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); 18947061Sbrian 19047461Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); 19147461Sbrian return &dev->dev; 19247461Sbrian } 19347061Sbrian 19447461Sbrian return NULL; 19547061Sbrian} 19647061Sbrian 19747061Sbrianstatic struct udpdevice * 19847061Sbrianudp_CreateDevice(struct physical *p, char *host, char *port) 19947061Sbrian{ 20047061Sbrian struct udpdevice *dev; 20147061Sbrian struct servent *sp; 20247061Sbrian 20347061Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 20447061Sbrian log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n", 20547061Sbrian p->link.name, strerror(errno)); 20647061Sbrian return NULL; 20747061Sbrian } 20847061Sbrian 20947061Sbrian dev->sock.sin_family = AF_INET; 21047061Sbrian dev->sock.sin_addr.s_addr = inet_addr(host); 21147061Sbrian dev->sock.sin_addr = GetIpAddr(host); 21247061Sbrian if (dev->sock.sin_addr.s_addr == INADDR_NONE) { 21347061Sbrian log_Printf(LogWARN, "%s: %s: unknown host\n", p->link.name, host); 21447061Sbrian free(dev); 21547061Sbrian return NULL; 21647061Sbrian } 21747061Sbrian dev->sock.sin_port = htons(atoi(port)); 21847061Sbrian if (dev->sock.sin_port == 0) { 21947061Sbrian sp = getservbyname(port, "udp"); 22047061Sbrian if (sp) 22147061Sbrian dev->sock.sin_port = sp->s_port; 22247061Sbrian else { 22347061Sbrian log_Printf(LogWARN, "%s: %s: unknown service\n", p->link.name, port); 22447061Sbrian free(dev); 22547061Sbrian return NULL; 22647061Sbrian } 22747061Sbrian } 22847061Sbrian 22947061Sbrian log_Printf(LogPHASE, "%s: Connecting to %s:%s/udp\n", p->link.name, 23047061Sbrian host, port); 23147061Sbrian 23289422Sbrian p->fd = socket(PF_INET, SOCK_DGRAM, 0); 23347061Sbrian if (p->fd >= 0) { 23447061Sbrian log_Printf(LogDEBUG, "%s: Opened udp socket %s\n", p->link.name, 23547061Sbrian p->name.full); 23647061Sbrian if (connect(p->fd, (struct sockaddr *)&dev->sock, sizeof dev->sock) == 0) { 23768424Sbrian dev->connected = UDP_CONNECTED; 23847061Sbrian return dev; 23947061Sbrian } else 24047061Sbrian log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno)); 24147061Sbrian } else 24247061Sbrian log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno)); 24347061Sbrian 24447061Sbrian close(p->fd); 24547061Sbrian p->fd = -1; 24647061Sbrian free(dev); 24747061Sbrian 24847061Sbrian return NULL; 24947061Sbrian} 25047061Sbrian 25147061Sbrianstruct device * 25247061Sbrianudp_Create(struct physical *p) 25347061Sbrian{ 25447061Sbrian char *cp, *host, *port, *svc; 25547061Sbrian struct udpdevice *dev; 25647061Sbrian 25747061Sbrian dev = NULL; 25847061Sbrian if (p->fd < 0) { 25952942Sbrian if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) { 26047061Sbrian *cp = '\0'; 26147061Sbrian host = p->name.full; 26247061Sbrian port = cp + 1; 26347061Sbrian svc = strchr(port, '/'); 26447061Sbrian if (svc && strcasecmp(svc, "/udp")) { 26547061Sbrian *cp = ':'; 26647061Sbrian return NULL; 26747061Sbrian } 26852942Sbrian if (svc) { 26952942Sbrian p->fd--; /* We own the device but maybe can't use it - change fd */ 27047061Sbrian *svc = '\0'; 27152942Sbrian } 27247061Sbrian 27347061Sbrian if (*host && *port) 27447061Sbrian dev = udp_CreateDevice(p, host, port); 27547061Sbrian 27647061Sbrian *cp = ':'; 27747061Sbrian if (svc) 27847061Sbrian *svc = '/'; 27947061Sbrian } 28047061Sbrian } else { 28147061Sbrian /* See if we're a connected udp socket */ 28271006Sbrian struct stat st; 28347061Sbrian 28471006Sbrian if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) { 28571006Sbrian int type, sz; 28671006Sbrian 28771006Sbrian sz = sizeof type; 28871006Sbrian if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) { 28971006Sbrian log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name); 29071006Sbrian close(p->fd); 29171006Sbrian p->fd = -1; 29247061Sbrian return NULL; 29347061Sbrian } 29447061Sbrian 29571006Sbrian if (sz == sizeof type && type == SOCK_DGRAM) { 29671006Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 29771006Sbrian log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n", 29871006Sbrian p->link.name, strerror(errno)); 29971006Sbrian return NULL; 30071006Sbrian } 30147061Sbrian 30271006Sbrian /* We can't getpeername().... */ 30371006Sbrian dev->connected = UDP_MAYBEUNCONNECTED; 30447061Sbrian 30571006Sbrian log_Printf(LogPHASE, "%s: Link is a udp socket\n", p->link.name); 30671006Sbrian 30771006Sbrian if (p->link.lcp.cfg.openmode != OPEN_PASSIVE) { 30871006Sbrian log_Printf(LogPHASE, "%s: Changing openmode to PASSIVE\n", 30971006Sbrian p->link.name); 31071006Sbrian p->link.lcp.cfg.openmode = OPEN_PASSIVE; 31171006Sbrian } 31247061Sbrian } 31347061Sbrian } 31447061Sbrian } 31547061Sbrian 31647061Sbrian if (dev) { 31747061Sbrian memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev); 31847461Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC); 31953733Sbrian if (p->cfg.cd.necessity != CD_DEFAULT) 32053733Sbrian log_Printf(LogWARN, "Carrier settings ignored\n"); 32147061Sbrian return &dev->dev; 32247061Sbrian } 32347061Sbrian 32447061Sbrian return NULL; 32547061Sbrian} 326