From 541106272ab5a1e04f69072f107522b8c13ec1c2 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 31 Jan 2025 19:09:39 +0000 Subject: [PATCH 01/13] ext/sockets: follow-up on AF_PACKET support. support from socket_recvfrom and exposing basic PHP userland type. --- ext/sockets/config.m4 | 2 +- ext/sockets/php_sockets.h | 4 ++ ext/sockets/sockets.c | 72 +++++++++++++++++++++++++++++++---- ext/sockets/sockets.stub.php | 14 +++++++ ext/sockets/sockets_arginfo.h | 39 ++++++++++++++++++- 5 files changed, 122 insertions(+), 9 deletions(-) diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index 37f927a78186a..a4a6e55a7926b 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets], if test "$PHP_SOCKETS" != "no"; then AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark]) - AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h]) + AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h netinet/ether.h]) AC_DEFINE([HAVE_SOCKETS], [1], [Define to 1 if the PHP extension 'sockets' is available.]) diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h index 89c613ba43074..550a8d488b06e 100644 --- a/ext/sockets/php_sockets.h +++ b/ext/sockets/php_sockets.h @@ -77,6 +77,10 @@ typedef struct { extern PHP_SOCKETS_API zend_class_entry *socket_ce; +#ifdef AF_PACKET +extern PHP_SOCKETS_API zend_class_entry *socket_ethinfo_ce; +#endif + static inline php_socket *socket_from_obj(zend_object *obj) { return (php_socket *)((char *)(obj) - XtOffsetOf(php_socket, std)); } diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 8a347bdfe45f8..8b44580505bb7 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -41,6 +41,7 @@ # include # include # include +# include # include # include # include @@ -54,6 +55,10 @@ # ifdef HAVE_IF_NAMETOINDEX # include # endif +# ifdef HAVE_NETINET_ETHER_H +# include +# include +# endif # if defined(HAVE_LINUX_SOCK_DIAG_H) # include # else @@ -120,6 +125,9 @@ static PHP_RSHUTDOWN_FUNCTION(sockets); zend_class_entry *socket_ce; static zend_object_handlers socket_object_handlers; +#ifdef AF_PACKET +zend_class_entry *socket_ethinfo_ce; +#endif static zend_object *socket_create_object(zend_class_entry *class_type) { php_socket *intern = zend_object_alloc(sizeof(php_socket), class_type); @@ -482,6 +490,9 @@ static PHP_MINIT_FUNCTION(sockets) socket_object_handlers.get_gc = socket_get_gc; socket_object_handlers.compare = zend_objects_not_comparable; +#if defined(AF_PACKET) + socket_ethinfo_ce = register_class_SocketEthernetInfo(); +#endif address_info_ce = register_class_AddressInfo(); address_info_ce->create_object = address_info_create_object; address_info_ce->default_object_handlers = &address_info_object_handlers; @@ -1503,7 +1514,7 @@ PHP_FUNCTION(socket_recvfrom) struct sockaddr_in6 sin6; #endif #ifdef AF_PACKET - //struct sockaddr_ll sll; + struct sockaddr_ll sll; #endif char addrbuf[INET6_ADDRSTRLEN]; socklen_t slen; @@ -1532,6 +1543,12 @@ PHP_FUNCTION(socket_recvfrom) RETURN_FALSE; } +#ifdef AF_PACKET + if (php_sock->type == AF_PACKET && arg3 < 2048) { + RETURN_FALSE; + } +#endif + recv_buf = zend_string_alloc(arg3 + 1, 0); switch (php_sock->type) { @@ -1610,7 +1627,6 @@ PHP_FUNCTION(socket_recvfrom) break; #endif #ifdef AF_PACKET - /* case AF_PACKET: // TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland // ditto for socket_sendto @@ -1618,6 +1634,7 @@ PHP_FUNCTION(socket_recvfrom) memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; char ifrname[IFNAMSIZ]; + zval zpayload; retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen); @@ -1626,8 +1643,6 @@ PHP_FUNCTION(socket_recvfrom) zend_string_efree(recv_buf); RETURN_FALSE; } - ZSTR_LEN(recv_buf) = retval; - ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) { PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno); @@ -1635,11 +1650,54 @@ PHP_FUNCTION(socket_recvfrom) RETURN_FALSE; } - ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf); + struct ethhdr *e = (struct ethhdr *)ZSTR_VAL(recv_buf); + unsigned short protocol = ntohs(e->h_proto); + unsigned char *payload = ((unsigned char *)e + sizeof(struct ethhdr)); + + object_init_ex(arg2, socket_ethinfo_ce); + zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); + zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); + array_init(&zpayload); + + switch (protocol) { + case ETH_P_IP: { + struct iphdr *ip = (struct iphdr *)payload; + unsigned char *ipdata = payload + (ip->ihl * 4); + struct in_addr s, d; + s.s_addr = ip->saddr; + d.s_addr = ip->daddr; + add_assoc_string(&zpayload, "ipsrc", inet_ntoa(s)); + add_assoc_string(&zpayload, "ipdst", inet_ntoa(d)); + + switch (ip->protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcp = (struct tcphdr *)ipdata; + add_assoc_long(&zpayload, "portsrc", ntohs(tcp->th_sport)); + add_assoc_long(&zpayload, "portdst", ntohs(tcp->th_dport)); + break; + } + case IPPROTO_UDP: { + struct udphdr *udp = (struct udphdr *)ipdata; + add_assoc_long(&zpayload, "portsrc", ntohs(udp->uh_sport)); + add_assoc_long(&zpayload, "portdst", ntohs(udp->uh_dport)); + break; + } + default: + zend_value_error("unsupported ip header protocol"); + RETURN_THROWS(); + } + break; + } + default: + zend_value_error("unsupported ethernet protocol"); + RETURN_THROWS(); + } + + zend_update_property(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("payload"), &zpayload); + ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex); break; - */ #endif default: zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); @@ -1751,7 +1809,7 @@ PHP_FUNCTION(socket_sendto) retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin)); break; - */ + */ #endif default: zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 4d6200fcd1ffe..1590329349466 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -2156,3 +2156,17 @@ function socket_wsaprotocol_info_import(string $info_id): Socket|false {} function socket_wsaprotocol_info_release(string $info_id): bool {} #endif + +#ifdef AF_PACKET +final class SocketEthernetInfo +{ + /** @readonly **/ + public Socket $socket; + /** @readonly **/ + public string $macsrc; + /** @readonly **/ + public string $macdst; + /** @readonly **/ + public array $payload; +} +#endif diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 4a90b4d789feb..33cd1dd242923 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: aac197335037777d31d83d4a4040bbfcd0c55813 */ + * Stub hash: 6f70f344542e0e2f2464fd024f7ca77415010c4e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -1126,3 +1126,40 @@ static zend_class_entry *register_class_AddressInfo(void) return class_entry; } + +#if defined(AF_PACKET) +static zend_class_entry *register_class_SocketEthernetInfo(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "SocketEthernetInfo", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + zval property_socket_default_value; + ZVAL_UNDEF(&property_socket_default_value); + zend_string *property_socket_name = zend_string_init("socket", sizeof("socket") - 1, 1); + zend_string *property_socket_class_Socket = zend_string_init("Socket", sizeof("Socket")-1, 1); + zend_declare_typed_property(class_entry, property_socket_name, &property_socket_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_socket_class_Socket, 0, 0)); + zend_string_release(property_socket_name); + + zval property_macsrc_default_value; + ZVAL_UNDEF(&property_macsrc_default_value); + zend_string *property_macsrc_name = zend_string_init("macsrc", sizeof("macsrc") - 1, 1); + zend_declare_typed_property(class_entry, property_macsrc_name, &property_macsrc_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_macsrc_name); + + zval property_macdst_default_value; + ZVAL_UNDEF(&property_macdst_default_value); + zend_string *property_macdst_name = zend_string_init("macdst", sizeof("macdst") - 1, 1); + zend_declare_typed_property(class_entry, property_macdst_name, &property_macdst_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_macdst_name); + + zval property_payload_default_value; + ZVAL_UNDEF(&property_payload_default_value); + zend_string *property_payload_name = zend_string_init("payload", sizeof("payload") - 1, 1); + zend_declare_typed_property(class_entry, property_payload_name, &property_payload_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_payload_name); + + return class_entry; +} +#endif From 93d0480cd0f403c2dd593046e2086d59dc2db98b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 2 Feb 2025 20:22:52 +0000 Subject: [PATCH 02/13] add more ethernet frame protocols support on reception --- ext/sockets/sockets.c | 27 +++++++++++++++++++++----- ext/sockets/tests/socket_afpacket.phpt | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 8b44580505bb7..f985d6337b2f1 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -58,6 +58,7 @@ # ifdef HAVE_NETINET_ETHER_H # include # include +# include # endif # if defined(HAVE_LINUX_SOCK_DIAG_H) # include @@ -1628,8 +1629,6 @@ PHP_FUNCTION(socket_recvfrom) #endif #ifdef AF_PACKET case AF_PACKET: - // TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland - // ditto for socket_sendto slen = sizeof(sll); memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; @@ -1688,6 +1687,24 @@ PHP_FUNCTION(socket_recvfrom) } break; } + case ETH_P_IPV6: { + struct ipv6hdr *ip = (struct ipv6hdr *)payload; + char s[INET6_ADDRSTRLEN], d[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ip->saddr, s, sizeof(s)); + inet_ntop(AF_INET6, &ip->daddr, d, sizeof(d)); + add_assoc_string(&zpayload, "ipsrc", s); + add_assoc_string(&zpayload, "ipdst", d); + break; + } + case ETH_P_LOOP: { + struct iphdr *ip = (struct iphdr *)payload; + struct in_addr s, d; + s.s_addr = ip->saddr; + d.s_addr = ip->daddr; + add_assoc_string(&zpayload, "ipsrc", inet_ntoa(s)); + add_assoc_string(&zpayload, "ipdst", inet_ntoa(d)); + break; + } default: zend_value_error("unsupported ethernet protocol"); RETURN_THROWS(); @@ -1719,7 +1736,7 @@ PHP_FUNCTION(socket_sendto) struct sockaddr_in6 sin6; #endif #ifdef AF_PACKET - //struct sockaddr_ll sll; + struct sockaddr_ll sll; #endif int retval; size_t buf_len; @@ -1796,7 +1813,6 @@ PHP_FUNCTION(socket_sendto) break; #endif #ifdef AF_PACKET - /* case AF_PACKET: if (port_is_null) { zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET"); @@ -1806,10 +1822,11 @@ PHP_FUNCTION(socket_sendto) memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = port; + sll.sll_halen = ETH_ALEN; + // TODO allows to use more user friendly type to replace raw buffer usage retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin)); break; - */ #endif default: zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index 2e83a654766e0..cd47adc94cc82 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -15,7 +15,7 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { ?> --FILE-- Date: Mon, 3 Feb 2025 07:26:21 +0000 Subject: [PATCH 03/13] expose needed ETH_FRAME_LEN, fix socket_sendto, test. --- ext/sockets/sockets.c | 3 ++- ext/sockets/sockets.stub.php | 7 +++++++ ext/sockets/sockets_arginfo.h | 11 ++++++++++- ext/sockets/tests/socket_afpacket.phpt | 13 +++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index f985d6337b2f1..bbf31e5fc0b32 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1656,6 +1656,7 @@ PHP_FUNCTION(socket_recvfrom) object_init_ex(arg2, socket_ethinfo_ce); zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); + zend_update_property_long(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("ethprotocol"), protocol); array_init(&zpayload); switch (protocol) { @@ -1825,7 +1826,7 @@ PHP_FUNCTION(socket_sendto) sll.sll_halen = ETH_ALEN; // TODO allows to use more user friendly type to replace raw buffer usage - retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin)); + retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sll, sizeof(sll)); break; #endif default: diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 1590329349466..8fd440dae6581 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -2013,6 +2013,11 @@ * @cvalue ETH_P_ALL */ const ETH_P_ALL = UNKNOWN; +/** + * @var int + * @cvalue ETH_FRAME_LEN + */ +const ETH_FRAME_LEN = UNKNOWN; #endif /** @@ -2163,6 +2168,8 @@ final class SocketEthernetInfo /** @readonly **/ public Socket $socket; /** @readonly **/ + public int $ethprotocol; + /** @readonly **/ public string $macsrc; /** @readonly **/ public string $macdst; diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 33cd1dd242923..1b2139838d351 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6f70f344542e0e2f2464fd024f7ca77415010c4e */ + * Stub hash: 9725b1a979cb5aa48079585278fa89c5edb321d4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -1105,6 +1105,9 @@ static void register_sockets_symbols(int module_number) #if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_ALL", ETH_P_ALL, CONST_PERSISTENT); #endif +#if defined(ETH_P_ALL) + REGISTER_LONG_CONSTANT("ETH_FRAME_LEN", ETH_FRAME_LEN, CONST_PERSISTENT); +#endif } static zend_class_entry *register_class_Socket(void) @@ -1142,6 +1145,12 @@ static zend_class_entry *register_class_SocketEthernetInfo(void) zend_declare_typed_property(class_entry, property_socket_name, &property_socket_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_socket_class_Socket, 0, 0)); zend_string_release(property_socket_name); + zval property_ethprotocol_default_value; + ZVAL_UNDEF(&property_ethprotocol_default_value); + zend_string *property_ethprotocol_name = zend_string_init("ethprotocol", sizeof("ethprotocol") - 1, 1); + zend_declare_typed_property(class_entry, property_ethprotocol_name, &property_ethprotocol_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_ethprotocol_name); + zval property_macsrc_default_value; ZVAL_UNDEF(&property_macsrc_default_value); zend_string *property_macsrc_name = zend_string_init("macsrc", sizeof("macsrc") - 1, 1); diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index cd47adc94cc82..b186004f8a77f 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -26,6 +26,18 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { var_dump($iindex); socket_getpeername($s_c, $istr2, $iindex2); + + $s_s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); + $v_bind = socket_bind($s_s, 'lo'); + + $buf = str_pad(str_repeat("0", ETH_FRAME_LEN) . + str_repeat("\xFF", 6) . + str_repeat("\x11", 6) . + "\x08\x00TEST ethernet", 2048, "\x00"); + + var_dump(socket_sendto($s_s, $buf, strlen($buf), 0, "lo", 1)); + + socket_close($s_s); socket_close($s_c); ?> --EXPECTF-- @@ -35,3 +47,4 @@ string(2) "lo" int(%i) Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported in %s on line %d +int(2048) From cb629748ab6177d598c0391ebf12bd3c2da72650 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 3 Feb 2025 14:49:44 +0000 Subject: [PATCH 04/13] fix object creation --- ext/sockets/sockets.c | 17 +++++++++++------ ext/sockets/tests/socket_afpacket.phpt | 7 ++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index bbf31e5fc0b32..9115354781f46 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1653,10 +1653,11 @@ PHP_FUNCTION(socket_recvfrom) unsigned short protocol = ntohs(e->h_proto); unsigned char *payload = ((unsigned char *)e + sizeof(struct ethhdr)); - object_init_ex(arg2, socket_ethinfo_ce); - zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); - zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); - zend_update_property_long(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("ethprotocol"), protocol); + zval obj; + object_init_ex(&obj, socket_ethinfo_ce); + zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); + zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); + zend_update_property_long(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("ethprotocol"), protocol); array_init(&zpayload); switch (protocol) { @@ -1711,10 +1712,14 @@ PHP_FUNCTION(socket_recvfrom) RETURN_THROWS(); } - zend_update_property(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("payload"), &zpayload); + zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("payload"), &zpayload); + ZEND_TRY_ASSIGN_REF_COPY(arg2, &obj); ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); - ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex); + + if (arg6) { + ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex); + } break; #endif default: diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index b186004f8a77f..ca2876c8d9bcb 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -30,10 +30,11 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { $s_s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); $v_bind = socket_bind($s_s, 'lo'); - $buf = str_pad(str_repeat("0", ETH_FRAME_LEN) . + $buf = str_repeat("0", ETH_FRAME_LEN) . str_repeat("\xFF", 6) . str_repeat("\x11", 6) . - "\x08\x00TEST ethernet", 2048, "\x00"); + "\x08\x00" . + str_pad(str_repeat("test", 512), 2048, "\x00"); var_dump(socket_sendto($s_s, $buf, strlen($buf), 0, "lo", 1)); @@ -47,4 +48,4 @@ string(2) "lo" int(%i) Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported in %s on line %d -int(2048) +int(3576) From e017a32f6759ca05a502046f587b0ef7cbbe3410 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 3 Feb 2025 19:08:24 +0000 Subject: [PATCH 05/13] fix leaks on failure --- ext/sockets/sockets.c | 17 ++++++++++++----- ext/sockets/tests/socket_afpacket.phpt | 16 +--------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 9115354781f46..d8d285cac1793 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1655,9 +1655,6 @@ PHP_FUNCTION(socket_recvfrom) zval obj; object_init_ex(&obj, socket_ethinfo_ce); - zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); - zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); - zend_update_property_long(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("ethprotocol"), protocol); array_init(&zpayload); switch (protocol) { @@ -1708,10 +1705,16 @@ PHP_FUNCTION(socket_recvfrom) break; } default: + zend_string_efree(recv_buf); + zval_ptr_dtor(&zpayload); + zval_ptr_dtor(&obj); zend_value_error("unsupported ethernet protocol"); RETURN_THROWS(); } + zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); + zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); + zend_update_property_long(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("ethprotocol"), protocol); zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("payload"), &zpayload); ZEND_TRY_ASSIGN_REF_COPY(arg2, &obj); @@ -1743,6 +1746,7 @@ PHP_FUNCTION(socket_sendto) #endif #ifdef AF_PACKET struct sockaddr_ll sll; + unsigned char halen; #endif int retval; size_t buf_len; @@ -1825,17 +1829,20 @@ PHP_FUNCTION(socket_sendto) RETURN_THROWS(); } + halen = addr_len > ETH_ALEN ? ETH_ALEN : (unsigned char)addr_len; + memset(&sll, 0, sizeof(sll)); + memcpy(sll.sll_addr, addr, halen); sll.sll_family = AF_PACKET; sll.sll_ifindex = port; - sll.sll_halen = ETH_ALEN; + sll.sll_halen = halen; // TODO allows to use more user friendly type to replace raw buffer usage retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sll, sizeof(sll)); break; #endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, AF_PACKET or AF_INET6"); RETURN_THROWS(); } diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index ca2876c8d9bcb..2e83a654766e0 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -15,7 +15,7 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { ?> --FILE-- --EXPECTF-- @@ -48,4 +35,3 @@ string(2) "lo" int(%i) Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported in %s on line %d -int(3576) From e4de953f0d76aa1cf6f99b1ca4d3aba8c491f763 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 3 Feb 2025 23:45:10 +0000 Subject: [PATCH 06/13] update test --- ext/sockets/sockets.c | 4 ++-- ext/sockets/tests/socket_afpacket.phpt | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index d8d285cac1793..f2f8a4cd6205b 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1706,8 +1706,8 @@ PHP_FUNCTION(socket_recvfrom) } default: zend_string_efree(recv_buf); - zval_ptr_dtor(&zpayload); - zval_ptr_dtor(&obj); + zval_ptr_dtor(&zpayload); + zval_ptr_dtor(&obj); zend_value_error("unsupported ethernet protocol"); RETURN_THROWS(); } diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index 2e83a654766e0..9d33b2cb0c3a9 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -26,6 +26,18 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { var_dump($iindex); socket_getpeername($s_c, $istr2, $iindex2); + + $s_s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); + $v_bind = socket_bind($s_s, 'lo'); + + $buf = str_repeat("0", ETH_FRAME_LEN) . + str_repeat("\xFF", 6) . + str_repeat("\x11", 6) . + "\x08\x00" . + str_pad("TEST", 46, "\x00"); + + var_dump(socket_sendto($s_s, $buf, strlen($buf), 0, "lo", 1)); + socket_close($s_c); ?> --EXPECTF-- @@ -35,3 +47,4 @@ string(2) "lo" int(%i) Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported in %s on line %d +int(1574) From 4847c791b42b374668bd395ee42ddf329af9d132 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 7 Feb 2025 18:08:44 +0000 Subject: [PATCH 07/13] further leak fixes, removing AF_PACKET handling for addrinfo_connect. does not make sense, we just bind to raw packets. --- ext/sockets/sockets.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index f2f8a4cd6205b..7ed35e10ec28c 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1681,6 +1681,9 @@ PHP_FUNCTION(socket_recvfrom) break; } default: + zend_string_efree(recv_buf); + zval_ptr_dtor(&zpayload); + zval_ptr_dtor(&obj); zend_value_error("unsupported ip header protocol"); RETURN_THROWS(); } @@ -2968,8 +2971,6 @@ PHP_FUNCTION(socket_addrinfo_connect) ai = Z_ADDRESS_INFO_P(arg1); - PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family); - object_init_ex(return_value, socket_ce); php_sock = Z_SOCKET_P(return_value); From 23060fee2671e2c12efdbe5c8e25aba0dddc887a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 7 Feb 2025 18:55:13 +0000 Subject: [PATCH 08/13] little buffer adjustments boundaries before more important changes. --- ext/sockets/sockets.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 7ed35e10ec28c..6c0008415a727 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1545,8 +1545,11 @@ PHP_FUNCTION(socket_recvfrom) } #ifdef AF_PACKET - if (php_sock->type == AF_PACKET && arg3 < 2048) { - RETURN_FALSE; + // ethernet header + payload + // possibly follow-up PR SOCK_DGRAM + if (php_sock->type == AF_PACKET && arg3 < 1514) { + zend_argument_value_error(3, "must be at least 1514 for AF_PACKET"); + RETURN_THROWS(); } #endif @@ -1782,6 +1785,15 @@ PHP_FUNCTION(socket_sendto) RETURN_THROWS(); } +#ifdef AF_PACKET + // ether header + payload + // TODO dealing with SOCK_DGRAM + if (php_sock->type == AF_PACKET && len < 60) { + zend_argument_value_error(3, "must be at least 64 for AF_PACKET"); + RETURN_THROWS(); + } +#endif + switch (php_sock->type) { case AF_UNIX: memset(&s_un, 0, sizeof(s_un)); From df81dda9867978dba9ea483ec95bc1bba23dc3e6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 9 Feb 2025 22:07:46 +0000 Subject: [PATCH 09/13] test packet send/recv. --- ext/sockets/sockets.c | 8 +++--- ext/sockets/tests/socket_afpacket.phpt | 37 ++++++++++++++++++++------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 6c0008415a727..8e3f431a68a46 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1400,7 +1400,7 @@ PHP_FUNCTION(socket_bind) struct sockaddr_ll *sa = (struct sockaddr_ll *) sock_type; socklen_t sa_len = sizeof(sa); - if (getsockname(php_sock->bsd_socket, sock_type, &sa_len) < 0) { + if (getsockname(php_sock->bsd_socket, (struct sockaddr *)sa, &sa_len) < 0) { zend_value_error("invalid AF_PACKET socket"); RETURN_THROWS(); } @@ -1547,8 +1547,8 @@ PHP_FUNCTION(socket_recvfrom) #ifdef AF_PACKET // ethernet header + payload // possibly follow-up PR SOCK_DGRAM - if (php_sock->type == AF_PACKET && arg3 < 1514) { - zend_argument_value_error(3, "must be at least 1514 for AF_PACKET"); + if (php_sock->type == AF_PACKET && arg3 < 60) { + zend_argument_value_error(3, "must be at least 60 for AF_PACKET"); RETURN_THROWS(); } #endif @@ -1718,10 +1718,12 @@ PHP_FUNCTION(socket_recvfrom) RETURN_THROWS(); } + zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("socket"), arg1); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); zend_update_property_long(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("ethprotocol"), protocol); zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("payload"), &zpayload); + // TODO fix leaks ZEND_TRY_ASSIGN_REF_COPY(arg2, &obj); ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index 9d33b2cb0c3a9..186c97bd21ad7 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -15,7 +15,7 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) { ?> --FILE-- @@ -47,4 +48,24 @@ string(2) "lo" int(%i) Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported in %s on line %d -int(1574) +int(60) +int(60) +string(2) "lo" +object(SocketEthernetInfo)#3 (4) { + ["socket"]=> + object(Socket)#1 (0) { + } + ["ethprotocol"]=> + int(%i) + ["macsrc"]=> + string(11) "0:0:0:0:0:0" + ["macdst"]=> + string(%d) "%s:%s:%s:%s:%s:%s" + ["payload"]=> + array(2) { + ["ipsrc"]=> + string(%d) "%d.%d.%d.%d" + ["ipdst"]=> + string(%d) "%d.%d.%d.%d" + } +} From fe00896ce51d21927a2268d8577844420b8687aa Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 11 Feb 2025 19:30:44 +0000 Subject: [PATCH 10/13] fix ethernet frame loop case. --- ext/sockets/sockets.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 8e3f431a68a46..1c1c21f28f9a3 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1654,7 +1654,7 @@ PHP_FUNCTION(socket_recvfrom) struct ethhdr *e = (struct ethhdr *)ZSTR_VAL(recv_buf); unsigned short protocol = ntohs(e->h_proto); - unsigned char *payload = ((unsigned char *)e + sizeof(struct ethhdr)); + unsigned char *payload; zval obj; object_init_ex(&obj, socket_ethinfo_ce); @@ -1662,6 +1662,7 @@ PHP_FUNCTION(socket_recvfrom) switch (protocol) { case ETH_P_IP: { + payload = ((unsigned char *)e + sizeof(struct ethhdr)); struct iphdr *ip = (struct iphdr *)payload; unsigned char *ipdata = payload + (ip->ihl * 4); struct in_addr s, d; @@ -1693,6 +1694,7 @@ PHP_FUNCTION(socket_recvfrom) break; } case ETH_P_IPV6: { + payload = ((unsigned char *)e + sizeof(struct ethhdr)); struct ipv6hdr *ip = (struct ipv6hdr *)payload; char s[INET6_ADDRSTRLEN], d[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ip->saddr, s, sizeof(s)); @@ -1702,12 +1704,6 @@ PHP_FUNCTION(socket_recvfrom) break; } case ETH_P_LOOP: { - struct iphdr *ip = (struct iphdr *)payload; - struct in_addr s, d; - s.s_addr = ip->saddr; - d.s_addr = ip->daddr; - add_assoc_string(&zpayload, "ipsrc", inet_ntoa(s)); - add_assoc_string(&zpayload, "ipdst", inet_ntoa(d)); break; } default: @@ -1718,6 +1714,7 @@ PHP_FUNCTION(socket_recvfrom) RETURN_THROWS(); } + Z_DELREF(zpayload); zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("socket"), arg1); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); @@ -1725,7 +1722,7 @@ PHP_FUNCTION(socket_recvfrom) zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("payload"), &zpayload); // TODO fix leaks - ZEND_TRY_ASSIGN_REF_COPY(arg2, &obj); + ZEND_TRY_ASSIGN_REF_VALUE(arg2, &obj); ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); if (arg6) { From ba41f45ea111c62091e857d32ed8f90b3c504f13 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 11 Feb 2025 19:34:16 +0000 Subject: [PATCH 11/13] add inner ether frame for loopback interface data. --- ext/sockets/sockets.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 1c1c21f28f9a3..377dff7f9d613 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1704,6 +1704,9 @@ PHP_FUNCTION(socket_recvfrom) break; } case ETH_P_LOOP: { + struct ethhdr *innere = (struct ethhdr *)((unsigned char *)e + ETH_HLEN); + add_assoc_string(&zpayload, "macsrc", ether_ntoa((struct ether_addr *)innere->h_source)); + add_assoc_string(&zpayload, "macdst", ether_ntoa((struct ether_addr *)innere->h_dest)); break; } default: From eb41f3f574be410919093db51fdd57232de598b3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 11 Feb 2025 20:52:42 +0000 Subject: [PATCH 12/13] fix leaks and test --- ext/sockets/sockets.c | 8 ++++---- ext/sockets/tests/socket_afpacket.phpt | 9 +++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 377dff7f9d613..5eec4e169428e 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1685,9 +1685,9 @@ PHP_FUNCTION(socket_recvfrom) break; } default: - zend_string_efree(recv_buf); - zval_ptr_dtor(&zpayload); - zval_ptr_dtor(&obj); + zend_string_efree(recv_buf); + zval_ptr_dtor(&zpayload); + zval_ptr_dtor(&obj); zend_value_error("unsupported ip header protocol"); RETURN_THROWS(); } @@ -1718,12 +1718,12 @@ PHP_FUNCTION(socket_recvfrom) } Z_DELREF(zpayload); + zend_string_efree(recv_buf); zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("socket"), arg1); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source)); zend_update_property_string(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest)); zend_update_property_long(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("ethprotocol"), protocol); zend_update_property(Z_OBJCE(obj), Z_OBJ(obj), ZEND_STRL("payload"), &zpayload); - // TODO fix leaks ZEND_TRY_ASSIGN_REF_VALUE(arg2, &obj); ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt index 186c97bd21ad7..93623bcfe9e7a 100644 --- a/ext/sockets/tests/socket_afpacket.phpt +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -51,7 +51,7 @@ Warning: socket_getpeername(): unable to retrieve peer name [95]: %sot supported int(60) int(60) string(2) "lo" -object(SocketEthernetInfo)#3 (4) { +object(SocketEthernetInfo)#3 (%d) { ["socket"]=> object(Socket)#1 (0) { } @@ -62,10 +62,7 @@ object(SocketEthernetInfo)#3 (4) { ["macdst"]=> string(%d) "%s:%s:%s:%s:%s:%s" ["payload"]=> - array(2) { - ["ipsrc"]=> - string(%d) "%d.%d.%d.%d" - ["ipdst"]=> - string(%d) "%d.%d.%d.%d" + array(%d) { + %a } } From da14f3e36694b64589e2d48ec48b2d1889360ef1 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 24 Feb 2025 19:07:58 +0000 Subject: [PATCH 13/13] forbids non SOCK_RAW socket with AF_PACKET for the moment. --- ext/sockets/sockets.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 5eec4e169428e..9e7cadf5a8d86 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1516,6 +1516,8 @@ PHP_FUNCTION(socket_recvfrom) #endif #ifdef AF_PACKET struct sockaddr_ll sll; + int protoid; + socklen_t protoidlen = sizeof(protoid); #endif char addrbuf[INET6_ADDRSTRLEN]; socklen_t slen; @@ -1632,6 +1634,13 @@ PHP_FUNCTION(socket_recvfrom) #endif #ifdef AF_PACKET case AF_PACKET: + getsockopt(php_sock->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &protoid, &protoidlen); + + // TODO: SOCK_DGRAM support + if (protoid != SOCK_RAW) { + zend_argument_value_error(1, "must be SOCK_RAW socket type"); + RETURN_THROWS(); + } slen = sizeof(sll); memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; @@ -1755,6 +1764,8 @@ PHP_FUNCTION(socket_sendto) #ifdef AF_PACKET struct sockaddr_ll sll; unsigned char halen; + int protoid; + socklen_t protoidlen = sizeof(protoid); #endif int retval; size_t buf_len; @@ -1841,12 +1852,19 @@ PHP_FUNCTION(socket_sendto) #endif #ifdef AF_PACKET case AF_PACKET: + getsockopt(php_sock->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &protoid, &protoidlen); + + // TODO: SOCK_DGRAM support + if (protoid != SOCK_RAW) { + zend_argument_value_error(1, "must be SOCK_RAW socket type"); + RETURN_THROWS(); + } if (port_is_null) { zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET"); RETURN_THROWS(); } - halen = addr_len > ETH_ALEN ? ETH_ALEN : (unsigned char)addr_len; + halen = ZSTR_LEN(addr) > ETH_ALEN ? ETH_ALEN : (unsigned char)ZSTR_LEN(addr); memset(&sll, 0, sizeof(sll)); memcpy(sll.sll_addr, addr, halen);