Skip to content

Commit a0d761c

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 <rbryant@redhat.com>
1 parent e82ee40 commit a0d761c

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

tests/test_utils.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +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,
1920
memory_profiling, merge_async_iterators, sha256,
20-
supports_kw, swap_dict_values)
21+
split_zmq_path, supports_kw, swap_dict_values)
2122

2223
from .utils import create_new_process_for_each_test, error_on_warning
2324

@@ -662,3 +663,46 @@ def test_sha256(input: tuple, output: int):
662663

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