You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

en0网卡多次添加IPv6地址后出现「无法分配内存」错误

macOS单网卡IPv6地址数量限制问题

问题背景

在为en0网卡分配**Unique Local Address (ULA)**类型IPv6地址时,遇到以下问题:

  • 初始可正常添加多个地址,但添加第18个时触发Cannot allocate memory错误
  • 手动执行命令:
    sudo ifconfig en0 inet6 fc00:45bd::81aa:15c6 prefixlen 64 alias
    
    返回错误:
    ifconfig: ioctl (SIOCAIFADDR): Cannot allocate memory
    
  • 移除部分已分配地址后,可重新添加至第17个,第18个再次报错
  • 怀疑Duplicate Address Detection (DAD)导致,将所有DAD相关sysctl参数设为0:
    net.inet6.ip6.dad_count: 0
    net.inet6.ip6.dad_enhanced: 0
    net.inet6.ip6.nd6_dad_nonce_max_count: 0
    net.inet6.icmp6.nd6_optimistic_dad: 0
    
    问题依旧,且报错时地址已出现在网卡配置中
  • 使用Go程序和直接调用ioctl的C程序测试,均复现相同错误

Go测试代码

package main

import (
    "crypto/sha256"
    "fmt"
    "math/big"
    "math/rand"
    "net/netip"
    "os/exec"
)

func main() {
    i := 0
    for {
        i++
        command := exec.Command("sudo", "ifconfig", "en0", "inet6", "add", GenerateIPv6Address().String())
        out, err := command.CombinedOutput()

        if err != nil {
            fmt.Println("\u001B[33m", i, command.String(), "\u001B[0m")
            fmt.Println(fmt.Errorf(err.Error()))
            fmt.Println("\u001B[31m" + string(out) + "\u001B[0m")
            break
        } else {
            fmt.Println("\u001B[32m", i, command.String()+"\u001B[0m")
            fmt.Println(string(out))
        }
    }
}

func GenerateIPv6Address() netip.Addr {
    seedBytes := make([]byte, 8)
    _, err := rand.Read(seedBytes)
    if err != nil {
        return netip.Addr{}
    }

    hash := sha256.Sum256(seedBytes)
    seedValue := new(big.Int).SetBytes(hash[:8])

    randInt := seedValue.Uint64()
    group2 := randInt % 65534
    group3 := (randInt >> 16) % 65534
    group4 := (randInt >> 32) % 65534

    ipStr := fmt.Sprintf("fc00:%x::%x:%x", group2, group3, group4)

    if ip, err := netip.ParseAddr(ipStr); err == nil {
        return ip
    }

    return netip.Addr{}
}

C测试代码

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <sys/errno.h>
#include <stdint.h>

char* generateIPv6() {
    unsigned short group[8];
    char *buffer = malloc(40); // IPv6 max length is 39 characters + null terminator

    if (!buffer) {
        perror("Failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    group[0] = 0xfc00; // Unique local address prefix
    for (int i = 1; i < 8; i++) {
        group[i] = arc4random() % 65536;
    }

    // Format the IPv6 address into the buffer
    snprintf(buffer, 40, "%x:%x:%x:%x:%x:%x:%x:%x",
           group[0], group[1], group[2], group[3],
           group[4], group[5], group[6], group[7]);

    return buffer; // Return the dynamically allocated address
}

int main() {
    int sockfd;
    struct in6_aliasreq ifr6;
    const char *interface = "en0";
    int prefix_len = 64;

    // Create socket
    sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Socket creation failed");
        return 1;
    }

    // Infinite loop to generate and add IPv6 addresses
    while (1) {
        char *ip_address = generateIPv6();

        // Prepare interface request
        memset(&ifr6, 0, sizeof(ifr6));
        strncpy(ifr6.ifra_name, interface, IFNAMSIZ);

        if (inet_pton(AF_INET6, ip_address, &ifr6.ifra_addr.sin6_addr) != 1) {
            perror("Invalid IP address");
            free(ip_address);
            close(sockfd);
            return 1;
        }

        ifr6.ifra_addr.sin6_len = sizeof ifr6.ifra_addr;
        ifr6.ifra_addr.sin6_family = AF_INET6;
        ifr6.ifra_prefixmask.sin6_family = AF_INET6;
        ifr6.ifra_prefixmask.sin6_len = sizeof ifr6.ifra_prefixmask;
        memset(&ifr6.ifra_prefixmask.sin6_addr, 0xFF, prefix_len / 8);

        // Set infinite lifetime
        ifr6.ifra_lifetime.ia6t_vltime = ifr6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;

        // Perform ioctl to add the address
        if (ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6) < 0) {
            perror("Failed to add IPv6 address");
            fprintf(stderr, "Error: %s\n", strerror(errno));
            free(ip_address);
            break; // Exit loop on error
        }

        printf("IPv6 address added successfully: %s\n", ip_address);
        free(ip_address);
    }

    close(sockfd);
    return 0;
}

C程序测试输出

$ sudo ktrace trace -Ss -f C4 -c ./output/bind_ipv6_using_syscall | grep bind_ipv6_using_sys

...
IPv6 address added successfully: fc00:2452:fc7e:5604:3d5c:ee50:a275:1ee3
IPv6 address added successfully: fc00:a332:1532:86bc:4f09:c01:b845:1685
IPv6 address added successfully: fc00:5f4c:dcfe:100:4542:9626:430c:de7f
IPv6 address added successfully: fc00:eb40:218a:6561:8f5a:ebab:7ed1:d3dc
IPv6 address added successfully: fc00:3422:b629:5d3a:7b69:caf0:4524:a18d
IPv6 address added successfully: fc00:cd47:a34b:f045:9620:2edd:9bc4:49f5
IPv6 address added successfully: fc00:305d:582c:94ff:d0fe:6d6f:b7df:31e1
IPv6 address added successfully: fc00:42e2:4920:93cc:6dec:8b3c:f34f:b7ef
IPv6 address added successfully: fc00:e1c3:dd39:53e:64f2:f047:8135:ee6c
IPv6 address added successfully: fc00:91ee:9b52:7cd0:862f:9101:84fd:9935
Failed to add IPv6 address: Cannot allocate memory
Error: Cannot allocate memory

问题解答

1. 是否由内核/系统对单接口IPv6地址数量的限制导致?

是。macOS内核对单网络接口的IPv6地址数量存在硬编码上限,测试中固定在第18个地址触发错误,正是触发了这个内核限制。错误提示的“Cannot allocate memory”并非指系统内存不足,而是内核分配给该接口的IPv6地址结构内存配额耗尽。

2. macOS中该限制是否可调整?

默认情况下无法调整。这个上限是内核代码中的硬编码值,没有提供用户空间可修改的sysctl参数或配置项。现代macOS受系统完整性保护(SIP)限制,用户无法自行编译修改内核参数的自定义内核,因此官方渠道下无法调整该限制。

3. 排查工具与日志

  • 内核日志排查:执行以下命令查看内核层面的详细日志,可找到触发限制时的内核报错信息:
    log show --predicate 'process == "kernel"' --info --debug --last 1h
    
  • 系统调用跟踪:使用dtruss跟踪程序的系统调用过程,查看ioctl失败时的详细上下文:
    sudo dtruss ./output/bind_ipv6_using_syscall
    
  • 内核参数检查:查看所有IPv6相关内核参数,确认是否存在隐藏的配置项(目前已知无此类可调整参数):
    sysctl -a | grep net.inet6
    
  • 实时地址数量验证:用以下命令查看当前接口已分配的IPv6地址数量,确认触发错误时的数量是否一致:
    ifconfig en0 inet6 | grep net6 | wc -l
    

内容的提问来源于stack exchange,提问作者Mohit Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:25:53