2012-05-24 29 views
16

Tôi đang sử dụng chức năng C sau để tạo nhiều không gian tên mạng từ một quá trình trường hợp duy nhất :Làm thế nào để tạo ra nhiều không gian tên mạng từ một trường hợp quá trình duy nhất

void create_namespace(const char *ns_name) 
{ 
    char ns_path[100]; 

    snprintf(ns_path, 100, "%s/%s", "/var/run/netns", ns_name); 
    close(open(ns_path, O_RDONLY|O_CREAT|O_EXCL, 0)); 
    unshare(CLONE_NEWNET); 
    mount("/proc/self/ns/net", ns_path, "none", MS_BIND , NULL); 
} 

Sau khi quá trình của tôi tạo ra tất cả các namspaces và Tôi thêm một giao diện vào bất kỳ không gian tên mạng nào (với lệnh ip link set tap1 netns ns1), sau đó tôi thực sự thấy giao diện này trong tất cả các không gian tên (có lẽ, đây thực sự là một không gian tên duy nhất nằm dưới các tên khác nhau).

Nhưng, nếu tôi tạo nhiều không gian tên bằng cách sử dụng nhiều quy trình thì mọi thứ đều hoạt động tốt.

Điều gì có thể xảy ra ở đây? Tôi có phải chuyển bất kỳ cờ bổ sung nào cho unshare() để làm việc này từ một cá thể quá trình không? Có một hạn chế rằng một cá thể quá trình đơn lẻ không thể tạo ra nhiều không gian tên mạng? Hoặc có vấn đề với cuộc gọi mount(), bởi vì /proc/self/ns/net thực sự được gắn nhiều lần?

Cập nhật: Dường như unshare() chức năng tạo ra nhiều không gian tên mạng một cách chính xác, nhưng tất cả các điểm gắn kết trong /var/run/netns/ thực sự tham khảo để không gian tên mạng đầu tiên được gắn trong direcotry đó.

Update2: Dường như cách tốt nhất là dùng fork() một quy trình khác và thực hiện hàm create_namespace() từ đó. Dù sao, tôi sẽ vui mừng khi nghe một giải pháp tốt hơn mà không liên quan đến fork() gọi hoặc ít nhất có được một xác nhận rằng sẽ chứng minh rằng nó là không thể tạo và quản lý nhiều không gian tên mạng từ một quá trình duy nhất.

UPDATE3: tôi có thể tạo ra nhiều không gian tên với không chia sẻ() bằng cách sử dụng đoạn mã sau:

int main() { 
    create_namespace("a"); 
    system("ip tuntap add mode tap tapa"); 
    system("ifconfig -a");//shows lo and tapA interface 
    create_namespace("b"); 
    system("ip tuntap add mode tap tapb"); 
    system("ifconfig -a");//show lo and tapB interface, but does not show tapA. So this is second namespace created. 
} 

Nhưng sau khi quá trình này kết thúc và tôi thực hiện ip netns exec a ifconfig -aip netns exec b ifconfig -a có vẻ như rằng cả hai lệnh là đột nhiên được thực hiện trong không gian tên a. Vì vậy, vấn đề thực tế là lưu trữ các tham chiếu đến các không gian tên (hoặc gọi mount() đúng cách. Nhưng tôi không chắc chắn, nếu điều này là có thể).

Trả lời

10

Bạn chỉ phải gắn kết gắn kết /proc/*/ns/* nếu bạn cần truy cập các không gian tên này từ một quy trình khác hoặc cần xử lý để có thể chuyển đổi qua lại giữa hai quy trình này. Không cần thiết phải sử dụng nhiều không gian tên từ một tiến trình đơn lẻ.

  • unshare không tạo vùng tên mới.
  • bản sao và ngã ba theo mặc định không tạo bất kỳ không gian tên mới nào.
  • có một không gian tên "hiện tại" của mỗi loại được gán cho một quy trình. Nó có thể được thay đổi bởi unshare hoặc setns. Đặt các không gian tên (theo mặc định) được kế thừa bởi các tiến trình con.

Bất cứ khi nào bạn làm mở (/proc/N/ns/net), nó tạo ra inode cho tập tin này, và tất cả mở tiếp theo() s sẽ trở lại tập tin đó là ràng buộc với cùng namespace . Thông tin chi tiết bị mất ở độ sâu của cache bộ nhớ cache hạt nhân.

Ngoài ra, mỗi quá trình chỉ có một mục nhập tệp /proc/self/ns/net và gắn kết không tạo ra phiên bản mới của tệp proc này. Mở các tệp được gắn là chính xác cùng một khi mở /proc/self/ns/net trực tiếp tệp (sẽ tiếp tục trỏ đến không gian tên được trỏ đến khi bạn mở lần đầu tiên).

Có vẻ như "/proc/*/ns" được nướng nửa chừng như thế này.

Vì vậy, nếu bạn chỉ cần 2 không gian tên, bạn có thể:

  • mở /proc/1/ns/net
  • không chia sẻ
  • mở /proc/self/ns/net

và chuyển đổi giữa hai người.

Để biết thêm 2 bạn có thể phải clone(). Dường như không có cách nào để tạo nhiều hơn một tệp /proc/N/ns/net cho mỗi quá trình.

Tuy nhiên, nếu bạn không cần phải chuyển qua lại giữa không gian tên trong thời gian chạy, hoặc để chia sẻ chúng với các quá trình khác, bạn có thể sử dụng nhiều không gian tên như thế này:

  • socket mở và các tiến trình chạy cho namespace chính.
  • không chia sẻ
  • socket mở và các quá trình chạy cho namespace 2 (netlink, tcp, vv)
  • không chia sẻ
  • ...
  • không chia sẻ
  • socket mở và các tiến trình chạy cho namespace thứ N (netlink, tcp, vv)

Ổ cắm mở giữ tham chiếu đến không gian tên mạng của chúng, vì vậy chúng sẽ không được thu thập cho đến khi các ổ cắm được đóng lại. Bạn cũng có thể sử dụng netlink để di chuyển các giao diện giữa các không gian tên, bằng cách gửi lệnh netlink trên vùng tên nguồn và chỉ định không gian tên dst bằng PID hoặc không gian tên FD (sau này bạn không có).

Bạn cần chuyển đổi không gian tên quá trình trước khi truy cập /proc mục nhập phụ thuộc vào không gian tên đó. Sau khi tệp "proc" được mở, nó giữ tham chiếu tới vùng tên.

17

Network Namespaces là, by design, tạo ra với một cuộc gọi đến bản sao, và nó có thể được sửa đổi sau bởi không chia sẻ. Lưu ý rằng ngay cả khi bạn tạo không gian tên mạng mới với unshare, trên thực tế bạn chỉ cần sửa đổi ngăn xếp mạng của quy trình đang chạy. unshare không thể sửa đổi ngăn xếp mạng của các quy trình khác, do đó bạn sẽ không thể tạo một mạng khác chỉ với unshare.

Để làm việc, không gian tên mạng mới cần một ngăn xếp mạng mới và do đó cần một quy trình mới. Đó là tất cả.

Tin tốt là nó có thể được thực hiện rất nhẹ với bản sao, see:

Clone() khác với truyền thống fork() gọi hệ thống trong UNIX, trong rằng nó cho phép quy trình cha mẹ và con chia sẻ có chọn lọc hoặc tài nguyên trùng lặp.

Bạn chỉ có thể chuyển hướng trên ngăn xếp mạng này (và tránh không gian bộ nhớ, bảng mô tả tệp và bảng xử lý tín hiệu). Quy trình mạng mới của bạn có thể được thực hiện giống như một chủ đề hơn một số thực tế ngã ba.

Bạn có thể thao tác chúng bằng mã C hoặc bằng công cụ Linux Kernel và/hoặc LXC.

Ví dụ, để thêm một thiết bị để namespace mạng mới, nó đơn giản như:

echo $PID > /sys/class/net/ethX/new_ns_pid 

Xem this page để biết thêm về CLI sẵn.

Ở phía bên C, bạn có thể xem triển khai lxc-unshare. Mặc dù tên của nó sử dụng tên bản sao, như bạn can see (lxc_clone là here). Người ta cũng có thể xem LTP implementation, nơi tác giả đã chọn sử dụng trực tiếp ngã ba.

EDIT: Có một mẹo mà bạn có thể sử dụng để làm cho chúng liên tục, nhưng bạn vẫn cần phải ngã ba, thậm chí tạm thời.

Hãy nhìn vào mã này của ipsource2 (Tôi đã gỡ bỏ kiểm tra lỗi cho rõ ràng):

snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); 

/* Create the base netns directory if it doesn't exist */ 
mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); 

/* Create the filesystem state */ 
fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0); 
[...] 
close(fd); 
unshare(CLONE_NEWNET); 
/* Bind the netns last so I can watch for it */ 
mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) 

Nếu bạn thực thi mã này trong một quá trình chia hai, bạn sẽ có thể tạo ra không gian tên mạng mới tại sẽ. Để xóa chúng, bạn có thể chỉ đơn giản là umount và xóa ràng buộc này:

umount2(netns_path, MNT_DETACH); 
if (unlink(netns_path) < 0) [...] 

EDIT2: khác (bẩn) lừa sẽ là cách đơn giản để thực hiện "ip netns thêm .." cli với hệ thống.

+0

+1, nhưng bạn có thể giải thích ý của mình với "Lưu ý rằng bạn không tạo không gian tên mạng mới với chia sẻ không"? Xem cập nhật # 3, bởi vì sự hiểu biết của tôi là unshare() vẫn có thể tạo không gian tên mạng.Bản sao (CLONE_NEWNET) giống như "Tôi sẽ tạo một tiến trình con mới với một không gian tên mạng mới", trong khi unshare (CLONE_NEWNET) giống như "Tôi không muốn chia sẻ không gian tên mạng với tiến trình cha mẹ của tôi nữa. cái mới. " lxc sử dụng clone(), trong khi iproute2 sử dụng unshare(). –

+0

Tôi sẽ cố gắng giải thích. Bạn tạo một không gian tên mạng cho quá trình _current_ của bạn với * unshare *, nhưng vì không gian tên mạng cần có PID để sống, bạn sẽ không thể tạo một vùng tên mới chỉ với * unshare * cho quá trình _same_. – Coren

+0

Tôi thấy bạn điểm, nhưng unshare() vẫn có thể tạo một không gian tên mạng mới (điều này cần cập nhật cho câu trả lời của bạn). Ngoài ra, tôi đoán, không gian tên không nhất thiết cần một PID thực tế để sống (ví dụ: sau khi thực hiện lệnh "ip netns add nsX", quá trình ip kết thúc, nhưng không gian tên nsX vẫn còn). Tôi đoán giới hạn này "tại sao nó không thể tạo ra nhiều không gian tên mạng từ quá trình duy nhất" đã làm điều gì đó với cách mount() hoạt động. –

Các vấn đề liên quan