int net_init_l2tpv3(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { /* FIXME error_setg(errp, ...) on failure */ const NetdevL2TPv3Options *l2tpv3; NetL2TPV3State *s; NetClientState *nc; int fd = -1, gairet; struct addrinfo hints; struct addrinfo *result = NULL; char *srcport, *dstport; nc = qemu_new_net_client(&net_l2tpv3_info, peer, "l2tpv3", name); s = DO_UPCAST(NetL2TPV3State, nc, nc); s->queue_head = 0; s->queue_tail = 0; s->header_mismatch = false; assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3); l2tpv3 = opts->u.l2tpv3; if (l2tpv3->has_ipv6 && l2tpv3->ipv6) { s->ipv6 = l2tpv3->ipv6; } else { s->ipv6 = false; } if ((l2tpv3->has_offset) && (l2tpv3->offset > 256)) { error_report("l2tpv3_open : offset must be less than 256 bytes"); goto outerr; } if (l2tpv3->has_rxcookie || l2tpv3->has_txcookie) { if (l2tpv3->has_rxcookie && l2tpv3->has_txcookie) { s->cookie = true; } else { goto outerr; } } else { s->cookie = false; } if (l2tpv3->has_cookie64 || l2tpv3->cookie64) { s->cookie_is_64 = true; } else { s->cookie_is_64 = false; } if (l2tpv3->has_udp && l2tpv3->udp) { s->udp = true; if (!(l2tpv3->has_srcport && l2tpv3->has_dstport)) { error_report("l2tpv3_open : need both src and dst port for udp"); goto outerr; } else { srcport = l2tpv3->srcport; dstport = l2tpv3->dstport; } } else { s->udp = false; srcport = NULL; dstport = NULL; } s->offset = 4; s->session_offset = 0; s->cookie_offset = 4; s->counter_offset = 4; s->tx_session = l2tpv3->txsession; if (l2tpv3->has_rxsession) { s->rx_session = l2tpv3->rxsession; } else { s->rx_session = s->tx_session; } if (s->cookie) { s->rx_cookie = l2tpv3->rxcookie; s->tx_cookie = l2tpv3->txcookie; if (s->cookie_is_64 == true) { /* 64 bit cookie */ s->offset += 8; s->counter_offset += 8; } else { /* 32 bit cookie */ s->offset += 4; s->counter_offset += 4; } } memset(&hints, 0, sizeof(hints)); if (s->ipv6) { hints.ai_family = AF_INET6; } else { hints.ai_family = AF_INET; } if (s->udp) { hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; s->offset += 4; s->counter_offset += 4; s->session_offset += 4; s->cookie_offset += 4; } else { hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_L2TP; } gairet = getaddrinfo(l2tpv3->src, srcport, &hints, &result); if ((gairet != 0) || (result == NULL)) { error_report( "l2tpv3_open : could not resolve src, errno = %s", gai_strerror(gairet) ); goto outerr; } fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (fd == -1) { fd = -errno; error_report("l2tpv3_open : socket creation failed, errno = %d", -fd); goto outerr; } if (bind(fd, (struct sockaddr *) result->ai_addr, result->ai_addrlen)) { error_report("l2tpv3_open : could not bind socket err=%i", errno); goto outerr; } if (result) { freeaddrinfo(result); } memset(&hints, 0, sizeof(hints)); if (s->ipv6) { hints.ai_family = AF_INET6; } else { hints.ai_family = AF_INET; } if (s->udp) { hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; } else { hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_L2TP; } result = NULL; gairet = getaddrinfo(l2tpv3->dst, dstport, &hints, &result); if ((gairet != 0) || (result == NULL)) { error_report( "l2tpv3_open : could not resolve dst, error = %s", gai_strerror(gairet) ); goto outerr; } s->dgram_dst = g_new0(struct sockaddr_storage, 1); memcpy(s->dgram_dst, result->ai_addr, result->ai_addrlen); s->dst_size = result->ai_addrlen; if (result) { freeaddrinfo(result); } if (l2tpv3->has_counter && l2tpv3->counter) { s->has_counter = true; s->offset += 4; } else { s->has_counter = false; } if (l2tpv3->has_pincounter && l2tpv3->pincounter) { s->has_counter = true; /* pin counter implies that there is counter */ s->pin_counter = true; } else { s->pin_counter = false; } if (l2tpv3->has_offset) { /* extra offset */ s->offset += l2tpv3->offset; } if ((s->ipv6) || (s->udp)) { s->header_size = s->offset; } else { s->header_size = s->offset + sizeof(struct iphdr); } s->msgvec = build_l2tpv3_vector(s, MAX_L2TPV3_MSGCNT); s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); qemu_set_nonblock(fd); s->fd = fd; s->counter = 0; l2tpv3_read_poll(s, true); snprintf(s->nc.info_str, sizeof(s->nc.info_str), "l2tpv3: connected"); return 0; outerr: qemu_del_net_client(nc); if (fd >= 0) { close(fd); } if (result) { freeaddrinfo(result); } return -1; }