Skip to content

Commit ea220c0

Browse files
committed
[Core] Enable IPv6 with vllm.utils.make_zmq_socket()
This code did not previously support IPv6 addresses for a TCP zmq socket. This fixes that, including some test cases. I spotted this while reading #15977. Signed-off-by: Russell Bryant <[email protected]>
1 parent e82ee40 commit ea220c0

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

tests/test_utils.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010

1111
import pytest
1212
import torch
13+
import zmq
1314
from vllm_test_utils.monitor import monitor
1415

1516
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
1617
from vllm.utils import (CacheInfo, FlexibleArgumentParser, LRUCache,
1718
MemorySnapshot, PlaceholderModule, StoreBoolean,
1819
bind_kv_cache, deprecate_kwargs, get_open_port,
19-
memory_profiling, merge_async_iterators, sha256,
20+
make_zmq_socket, memory_profiling,
21+
merge_async_iterators, sha256, split_zmq_path,
2022
supports_kw, swap_dict_values)
2123

2224
from .utils import create_new_process_for_each_test, error_on_warning
@@ -662,3 +664,46 @@ def test_sha256(input: tuple, output: int):
662664

663665
# hashing different input, returns different value
664666
assert hash != sha256(input + (1, ))
667+
668+
669+
@pytest.mark.parametrize(
670+
"path,expected",
671+
[
672+
("ipc://some_path", ("ipc", "some_path", "")),
673+
("tcp://127.0.0.1:5555", ("tcp", "127.0.0.1", "5555")),
674+
("tcp://[::1]:5555", ("tcp", "::1", "5555")), # IPv6 address
675+
("inproc://some_identifier", ("inproc", "some_identifier", "")),
676+
]
677+
)
678+
def test_split_zmq_path(path, expected):
679+
assert split_zmq_path(path) == expected
680+
681+
682+
@pytest.mark.parametrize(
683+
"invalid_path",
684+
[
685+
"invalid_path", # Missing scheme
686+
"tcp://127.0.0.1", # Missing port
687+
"tcp://[::1]", # Missing port for IPv6
688+
"tcp://:5555", # Missing host
689+
]
690+
)
691+
def test_split_zmq_path_invalid(invalid_path):
692+
with pytest.raises(ValueError):
693+
split_zmq_path(invalid_path)
694+
695+
696+
def test_make_zmq_socket_ipv6():
697+
ctx = zmq.Context()
698+
ipv6_path = "tcp://[::1]:5555" # IPv6 loopback address
699+
socket_type = zmq.REP # Example socket type
700+
701+
# Create the socket
702+
socket = make_zmq_socket(ctx, ipv6_path, socket_type)
703+
704+
# Verify that the IPV6 option is set
705+
assert socket.getsockopt(zmq.IPV6) == 1, "IPV6 option should be enabled for IPv6 addresses"
706+
707+
# Clean up
708+
socket.close()
709+
ctx.term()

vllm/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from types import MappingProxyType
4444
from typing import (TYPE_CHECKING, Any, Callable, Generic, Literal, NamedTuple,
4545
Optional, Tuple, Type, TypeVar, Union, cast, overload)
46+
from urllib.parse import urlparse
4647
from uuid import uuid4
4748

4849
import cachetools
@@ -2250,6 +2251,27 @@ def get_exception_traceback():
22502251
return err_str
22512252

22522253

2254+
def split_zmq_path(path: str) -> Tuple[str, str, str]:
2255+
"""Split a zmq path into its parts."""
2256+
parsed = urlparse(path)
2257+
if not parsed.scheme:
2258+
raise ValueError(f"Invalid zmq path: {path}")
2259+
2260+
scheme = parsed.scheme
2261+
host = parsed.hostname or ""
2262+
port = str(parsed.port or "")
2263+
2264+
if scheme == "tcp" and not all((host, port)):
2265+
# The host and port fields are required for tcp
2266+
raise ValueError(f"Invalid zmq path: {path}")
2267+
2268+
if scheme != "tcp" and port:
2269+
# port only makes sense with tcp
2270+
raise ValueError(f"Invalid zmq path: {path}")
2271+
2272+
return scheme, host, port
2273+
2274+
22532275
# Adapted from: https://github.com/sgl-project/sglang/blob/v0.4.1/python/sglang/srt/utils.py#L783 # noqa: E501
22542276
def make_zmq_socket(
22552277
ctx: Union[zmq.asyncio.Context, zmq.Context], # type: ignore[name-defined]
@@ -2289,6 +2311,12 @@ def make_zmq_socket(
22892311
if identity is not None:
22902312
socket.setsockopt(zmq.IDENTITY, identity)
22912313

2314+
# Determine if the path is a TCP socket with an IPv6 address.
2315+
# Enable IPv6 on the zmq socket if so.
2316+
scheme, host, _ = split_zmq_path(path)
2317+
if scheme == "tcp" and is_valid_ipv6_address(host):
2318+
socket.setsockopt(zmq.IPV6, 1)
2319+
22922320
if bind:
22932321
socket.bind(path)
22942322
else:

0 commit comments

Comments
 (0)