proto_tcp.c revision 210876
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 210876 2010-08-05 18:27:41Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> /* MAXHOSTNAMELEN */ 34204076Spjd 35204076Spjd#include <netinet/in.h> 36204076Spjd#include <netinet/tcp.h> 37204076Spjd 38204076Spjd#include <assert.h> 39204076Spjd#include <errno.h> 40207390Spjd#include <fcntl.h> 41204076Spjd#include <netdb.h> 42204076Spjd#include <stdbool.h> 43204076Spjd#include <stdint.h> 44204076Spjd#include <stdio.h> 45204076Spjd#include <string.h> 46204076Spjd#include <unistd.h> 47204076Spjd 48204076Spjd#include "hast.h" 49204076Spjd#include "pjdlog.h" 50204076Spjd#include "proto_impl.h" 51207390Spjd#include "subr.h" 52204076Spjd 53204076Spjd#define TCP4_CTX_MAGIC 0x7c441c 54204076Spjdstruct tcp4_ctx { 55204076Spjd int tc_magic; 56204076Spjd struct sockaddr_in tc_sin; 57204076Spjd int tc_fd; 58204076Spjd int tc_side; 59204076Spjd#define TCP4_SIDE_CLIENT 0 60204076Spjd#define TCP4_SIDE_SERVER_LISTEN 1 61204076Spjd#define TCP4_SIDE_SERVER_WORK 2 62204076Spjd}; 63204076Spjd 64204076Spjdstatic void tcp4_close(void *ctx); 65204076Spjd 66204076Spjdstatic in_addr_t 67204076Spjdstr2ip(const char *str) 68204076Spjd{ 69204076Spjd struct hostent *hp; 70204076Spjd in_addr_t ip; 71204076Spjd 72204076Spjd ip = inet_addr(str); 73204076Spjd if (ip != INADDR_NONE) { 74204076Spjd /* It is a valid IP address. */ 75204076Spjd return (ip); 76204076Spjd } 77204076Spjd /* Check if it is a valid host name. */ 78204076Spjd hp = gethostbyname(str); 79204076Spjd if (hp == NULL) 80204076Spjd return (INADDR_NONE); 81204076Spjd return (((struct in_addr *)(void *)hp->h_addr)->s_addr); 82204076Spjd} 83204076Spjd 84204076Spjd/* 85204076Spjd * Function converts the given string to unsigned number. 86204076Spjd */ 87204076Spjdstatic int 88204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 89204076Spjd{ 90204076Spjd intmax_t digit, num; 91204076Spjd 92204076Spjd if (str[0] == '\0') 93204076Spjd goto invalid; /* Empty string. */ 94204076Spjd num = 0; 95204076Spjd for (; *str != '\0'; str++) { 96204076Spjd if (*str < '0' || *str > '9') 97204076Spjd goto invalid; /* Non-digit character. */ 98204076Spjd digit = *str - '0'; 99204076Spjd if (num > num * 10 + digit) 100204076Spjd goto invalid; /* Overflow. */ 101204076Spjd num = num * 10 + digit; 102204076Spjd if (num > maxnum) 103204076Spjd goto invalid; /* Too big. */ 104204076Spjd } 105204076Spjd if (num < minnum) 106204076Spjd goto invalid; /* Too small. */ 107204076Spjd *nump = num; 108204076Spjd return (0); 109204076Spjdinvalid: 110204076Spjd errno = EINVAL; 111204076Spjd return (-1); 112204076Spjd} 113204076Spjd 114204076Spjdstatic int 115204076Spjdtcp4_addr(const char *addr, struct sockaddr_in *sinp) 116204076Spjd{ 117204076Spjd char iporhost[MAXHOSTNAMELEN]; 118204076Spjd const char *pp; 119204076Spjd size_t size; 120204076Spjd in_addr_t ip; 121204076Spjd 122204076Spjd if (addr == NULL) 123204076Spjd return (-1); 124204076Spjd 125204076Spjd if (strncasecmp(addr, "tcp4://", 7) == 0) 126204076Spjd addr += 7; 127204076Spjd else if (strncasecmp(addr, "tcp://", 6) == 0) 128204076Spjd addr += 6; 129210870Spjd else { 130210870Spjd /* 131210870Spjd * Because TCP4 is the default assume IP or host is given without 132210870Spjd * prefix. 133210870Spjd */ 134210870Spjd } 135204076Spjd 136204076Spjd sinp->sin_family = AF_INET; 137204076Spjd sinp->sin_len = sizeof(*sinp); 138204076Spjd /* Extract optional port. */ 139204076Spjd pp = strrchr(addr, ':'); 140204076Spjd if (pp == NULL) { 141204076Spjd /* Port not given, use the default. */ 142204076Spjd sinp->sin_port = htons(HASTD_PORT); 143204076Spjd } else { 144204076Spjd intmax_t port; 145204076Spjd 146204076Spjd if (numfromstr(pp + 1, 1, 65535, &port) < 0) 147204076Spjd return (errno); 148204076Spjd sinp->sin_port = htons(port); 149204076Spjd } 150204076Spjd /* Extract host name or IP address. */ 151204076Spjd if (pp == NULL) { 152204076Spjd size = sizeof(iporhost); 153204076Spjd if (strlcpy(iporhost, addr, size) >= size) 154204076Spjd return (ENAMETOOLONG); 155204076Spjd } else { 156204076Spjd size = (size_t)(pp - addr + 1); 157204076Spjd if (size > sizeof(iporhost)) 158204076Spjd return (ENAMETOOLONG); 159210876Spjd if (strlcpy(iporhost, addr, size) >= size) 160210876Spjd return (ENAMETOOLONG); 161204076Spjd } 162204076Spjd /* Convert string (IP address or host name) to in_addr_t. */ 163204076Spjd ip = str2ip(iporhost); 164204076Spjd if (ip == INADDR_NONE) 165204076Spjd return (EINVAL); 166204076Spjd sinp->sin_addr.s_addr = ip; 167204076Spjd 168204076Spjd return (0); 169204076Spjd} 170204076Spjd 171204076Spjdstatic int 172204076Spjdtcp4_common_setup(const char *addr, void **ctxp, int side) 173204076Spjd{ 174204076Spjd struct tcp4_ctx *tctx; 175204076Spjd int ret, val; 176204076Spjd 177204076Spjd tctx = malloc(sizeof(*tctx)); 178204076Spjd if (tctx == NULL) 179204076Spjd return (errno); 180204076Spjd 181204076Spjd /* Parse given address. */ 182204076Spjd if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) { 183204076Spjd free(tctx); 184204076Spjd return (ret); 185204076Spjd } 186204076Spjd 187204076Spjd tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); 188204076Spjd if (tctx->tc_fd == -1) { 189204076Spjd ret = errno; 190204076Spjd free(tctx); 191204076Spjd return (ret); 192204076Spjd } 193204076Spjd 194204076Spjd /* Socket settings. */ 195204076Spjd val = 1; 196204076Spjd if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val, 197204076Spjd sizeof(val)) == -1) { 198204076Spjd pjdlog_warning("Unable to set TCP_NOELAY on %s", addr); 199204076Spjd } 200204076Spjd val = 131072; 201204076Spjd if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val, 202204076Spjd sizeof(val)) == -1) { 203204076Spjd pjdlog_warning("Unable to set send buffer size on %s", addr); 204204076Spjd } 205204076Spjd val = 131072; 206204076Spjd if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val, 207204076Spjd sizeof(val)) == -1) { 208204076Spjd pjdlog_warning("Unable to set receive buffer size on %s", addr); 209204076Spjd } 210204076Spjd 211204076Spjd tctx->tc_side = side; 212204076Spjd tctx->tc_magic = TCP4_CTX_MAGIC; 213204076Spjd *ctxp = tctx; 214204076Spjd 215204076Spjd return (0); 216204076Spjd} 217204076Spjd 218204076Spjdstatic int 219204076Spjdtcp4_client(const char *addr, void **ctxp) 220204076Spjd{ 221204076Spjd 222204076Spjd return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT)); 223204076Spjd} 224204076Spjd 225204076Spjdstatic int 226204076Spjdtcp4_connect(void *ctx) 227204076Spjd{ 228204076Spjd struct tcp4_ctx *tctx = ctx; 229207390Spjd struct timeval tv; 230207390Spjd fd_set fdset; 231207390Spjd socklen_t esize; 232207390Spjd int error, flags, ret; 233204076Spjd 234204076Spjd assert(tctx != NULL); 235204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 236204076Spjd assert(tctx->tc_side == TCP4_SIDE_CLIENT); 237204076Spjd assert(tctx->tc_fd >= 0); 238204076Spjd 239207390Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 240207390Spjd if (flags == -1) { 241207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 242207390Spjd "fcntl(F_GETFL) failed")); 243204076Spjd return (errno); 244204076Spjd } 245207390Spjd /* 246207390Spjd * We make socket non-blocking so we have decided about connection 247207390Spjd * timeout. 248207390Spjd */ 249207390Spjd flags |= O_NONBLOCK; 250207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 251207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 252207390Spjd "fcntl(F_SETFL, O_NONBLOCK) failed")); 253207390Spjd return (errno); 254207390Spjd } 255204076Spjd 256207390Spjd if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 257207390Spjd sizeof(tctx->tc_sin)) == 0) { 258207390Spjd error = 0; 259207390Spjd goto done; 260207390Spjd } 261207390Spjd if (errno != EINPROGRESS) { 262207390Spjd error = errno; 263207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 264207390Spjd goto done; 265207390Spjd } 266207390Spjd /* 267207390Spjd * Connection can't be established immediately, let's wait 268207390Spjd * for HAST_TIMEOUT seconds. 269207390Spjd */ 270207390Spjd tv.tv_sec = HAST_TIMEOUT; 271207390Spjd tv.tv_usec = 0; 272207390Spjdagain: 273207390Spjd FD_ZERO(&fdset); 274207390Spjd FD_SET(tctx->tc_fd, &fdset); 275207390Spjd ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 276207390Spjd if (ret == 0) { 277207390Spjd error = ETIMEDOUT; 278207390Spjd goto done; 279207390Spjd } else if (ret == -1) { 280207390Spjd if (errno == EINTR) 281207390Spjd goto again; 282207390Spjd error = errno; 283207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 284207390Spjd goto done; 285207390Spjd } 286207390Spjd assert(ret > 0); 287207390Spjd assert(FD_ISSET(tctx->tc_fd, &fdset)); 288207390Spjd esize = sizeof(error); 289207390Spjd if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 290207390Spjd &esize) == -1) { 291207390Spjd error = errno; 292207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 293207390Spjd "getsockopt(SO_ERROR) failed"); 294207390Spjd goto done; 295207390Spjd } 296207390Spjd if (error != 0) { 297207390Spjd pjdlog_common(LOG_DEBUG, 1, error, 298207390Spjd "getsockopt(SO_ERROR) returned error"); 299207390Spjd goto done; 300207390Spjd } 301207390Spjd error = 0; 302207390Spjddone: 303207390Spjd flags &= ~O_NONBLOCK; 304207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 305207390Spjd if (error == 0) 306207390Spjd error = errno; 307207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 308207390Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 309207390Spjd } 310207390Spjd return (error); 311204076Spjd} 312204076Spjd 313204076Spjdstatic int 314204076Spjdtcp4_server(const char *addr, void **ctxp) 315204076Spjd{ 316204076Spjd struct tcp4_ctx *tctx; 317204076Spjd int ret, val; 318204076Spjd 319204076Spjd ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN); 320204076Spjd if (ret != 0) 321204076Spjd return (ret); 322204076Spjd 323204076Spjd tctx = *ctxp; 324204076Spjd 325204076Spjd val = 1; 326204076Spjd /* Ignore failure. */ 327204076Spjd (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 328204076Spjd sizeof(val)); 329204076Spjd 330204076Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 331204076Spjd sizeof(tctx->tc_sin)) < 0) { 332204076Spjd ret = errno; 333204076Spjd tcp4_close(tctx); 334204076Spjd return (ret); 335204076Spjd } 336204076Spjd if (listen(tctx->tc_fd, 8) < 0) { 337204076Spjd ret = errno; 338204076Spjd tcp4_close(tctx); 339204076Spjd return (ret); 340204076Spjd } 341204076Spjd 342204076Spjd return (0); 343204076Spjd} 344204076Spjd 345204076Spjdstatic int 346204076Spjdtcp4_accept(void *ctx, void **newctxp) 347204076Spjd{ 348204076Spjd struct tcp4_ctx *tctx = ctx; 349204076Spjd struct tcp4_ctx *newtctx; 350204076Spjd socklen_t fromlen; 351204076Spjd int ret; 352204076Spjd 353204076Spjd assert(tctx != NULL); 354204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 355204076Spjd assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN); 356204076Spjd assert(tctx->tc_fd >= 0); 357204076Spjd 358204076Spjd newtctx = malloc(sizeof(*newtctx)); 359204076Spjd if (newtctx == NULL) 360204076Spjd return (errno); 361204076Spjd 362204076Spjd fromlen = sizeof(tctx->tc_sin); 363204076Spjd newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 364204076Spjd &fromlen); 365204076Spjd if (newtctx->tc_fd < 0) { 366204076Spjd ret = errno; 367204076Spjd free(newtctx); 368204076Spjd return (ret); 369204076Spjd } 370204076Spjd 371204076Spjd newtctx->tc_side = TCP4_SIDE_SERVER_WORK; 372204076Spjd newtctx->tc_magic = TCP4_CTX_MAGIC; 373204076Spjd *newctxp = newtctx; 374204076Spjd 375204076Spjd return (0); 376204076Spjd} 377204076Spjd 378204076Spjdstatic int 379204076Spjdtcp4_send(void *ctx, const unsigned char *data, size_t size) 380204076Spjd{ 381204076Spjd struct tcp4_ctx *tctx = ctx; 382204076Spjd 383204076Spjd assert(tctx != NULL); 384204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 385204076Spjd assert(tctx->tc_fd >= 0); 386204076Spjd 387204076Spjd return (proto_common_send(tctx->tc_fd, data, size)); 388204076Spjd} 389204076Spjd 390204076Spjdstatic int 391204076Spjdtcp4_recv(void *ctx, unsigned char *data, size_t size) 392204076Spjd{ 393204076Spjd struct tcp4_ctx *tctx = ctx; 394204076Spjd 395204076Spjd assert(tctx != NULL); 396204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 397204076Spjd assert(tctx->tc_fd >= 0); 398204076Spjd 399204076Spjd return (proto_common_recv(tctx->tc_fd, data, size)); 400204076Spjd} 401204076Spjd 402204076Spjdstatic int 403204076Spjdtcp4_descriptor(const void *ctx) 404204076Spjd{ 405204076Spjd const struct tcp4_ctx *tctx = ctx; 406204076Spjd 407204076Spjd assert(tctx != NULL); 408204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 409204076Spjd 410204076Spjd return (tctx->tc_fd); 411204076Spjd} 412204076Spjd 413204076Spjdstatic void 414204076Spjdsin2str(struct sockaddr_in *sinp, char *addr, size_t size) 415204076Spjd{ 416204076Spjd in_addr_t ip; 417204076Spjd unsigned int port; 418204076Spjd 419204076Spjd assert(addr != NULL); 420204076Spjd assert(sinp->sin_family == AF_INET); 421204076Spjd 422204076Spjd ip = ntohl(sinp->sin_addr.s_addr); 423204076Spjd port = ntohs(sinp->sin_port); 424210876Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u", 425210876Spjd ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), 426210876Spjd (ip & 0xff), port) < (ssize_t)size); 427204076Spjd} 428204076Spjd 429204076Spjdstatic bool 430204076Spjdtcp4_address_match(const void *ctx, const char *addr) 431204076Spjd{ 432204076Spjd const struct tcp4_ctx *tctx = ctx; 433204076Spjd struct sockaddr_in sin; 434204076Spjd socklen_t sinlen; 435204076Spjd in_addr_t ip1, ip2; 436204076Spjd 437204076Spjd assert(tctx != NULL); 438204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 439204076Spjd 440204076Spjd if (tcp4_addr(addr, &sin) != 0) 441204076Spjd return (false); 442204076Spjd ip1 = sin.sin_addr.s_addr; 443204076Spjd 444204076Spjd sinlen = sizeof(sin); 445204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) 446204076Spjd return (false); 447204076Spjd ip2 = sin.sin_addr.s_addr; 448204076Spjd 449204076Spjd return (ip1 == ip2); 450204076Spjd} 451204076Spjd 452204076Spjdstatic void 453204076Spjdtcp4_local_address(const void *ctx, char *addr, size_t size) 454204076Spjd{ 455204076Spjd const struct tcp4_ctx *tctx = ctx; 456204076Spjd struct sockaddr_in sin; 457204076Spjd socklen_t sinlen; 458204076Spjd 459204076Spjd assert(tctx != NULL); 460204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 461204076Spjd 462204076Spjd sinlen = sizeof(sin); 463204076Spjd if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 464210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 465204076Spjd return; 466204076Spjd } 467204076Spjd sin2str(&sin, addr, size); 468204076Spjd} 469204076Spjd 470204076Spjdstatic void 471204076Spjdtcp4_remote_address(const void *ctx, char *addr, size_t size) 472204076Spjd{ 473204076Spjd const struct tcp4_ctx *tctx = ctx; 474204076Spjd struct sockaddr_in sin; 475204076Spjd socklen_t sinlen; 476204076Spjd 477204076Spjd assert(tctx != NULL); 478204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 479204076Spjd 480204076Spjd sinlen = sizeof(sin); 481204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 482210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 483204076Spjd return; 484204076Spjd } 485204076Spjd sin2str(&sin, addr, size); 486204076Spjd} 487204076Spjd 488204076Spjdstatic void 489204076Spjdtcp4_close(void *ctx) 490204076Spjd{ 491204076Spjd struct tcp4_ctx *tctx = ctx; 492204076Spjd 493204076Spjd assert(tctx != NULL); 494204076Spjd assert(tctx->tc_magic == TCP4_CTX_MAGIC); 495204076Spjd 496204076Spjd if (tctx->tc_fd >= 0) 497204076Spjd close(tctx->tc_fd); 498204076Spjd tctx->tc_magic = 0; 499204076Spjd free(tctx); 500204076Spjd} 501204076Spjd 502204076Spjdstatic struct hast_proto tcp4_proto = { 503204076Spjd .hp_name = "tcp4", 504204076Spjd .hp_client = tcp4_client, 505204076Spjd .hp_connect = tcp4_connect, 506204076Spjd .hp_server = tcp4_server, 507204076Spjd .hp_accept = tcp4_accept, 508204076Spjd .hp_send = tcp4_send, 509204076Spjd .hp_recv = tcp4_recv, 510204076Spjd .hp_descriptor = tcp4_descriptor, 511204076Spjd .hp_address_match = tcp4_address_match, 512204076Spjd .hp_local_address = tcp4_local_address, 513204076Spjd .hp_remote_address = tcp4_remote_address, 514204076Spjd .hp_close = tcp4_close 515204076Spjd}; 516204076Spjd 517204076Spjdstatic __constructor void 518204076Spjdtcp4_ctor(void) 519204076Spjd{ 520204076Spjd 521210869Spjd proto_register(&tcp4_proto, true); 522204076Spjd} 523