2017-11-25 62 views
5

Tôi đang cố gắng sử dụng cổng từ hai ứng dụng và yêu cầu mỗi gói nhận gói từ một nhóm địa chỉ IP khác. Để đạt được điều này, tôi sử dụng các tùy chọn SO_REUSEPORT và SO_ATTACH_REUSEPORT_CBPF socket. Mã của tôi là như sau:Hành vi không mong muốn SO_ATTACH_REUSEPORT_CBPF tùy chọn ổ cắm

parentfd = socket(AF_INET, SOCK_STREAM, 0); 
if (parentfd < 0) 
    error("ERROR opening socket"); 

struct sock_filter code[]={ 
    { 0x28, 0, 0, 0x0000000c }, 
    { 0x15, 0, 3, 0x00000800 }, 
    { 0x20, 0, 0, 0x0000001a }, 
    { 0x15, 2, 0, 0xc0a8ff01 }, 
    { 0x6, 0, 0, 0x00000000 }, 
    { 0x6, 0, 0, 0x00040000 }, 
    { 0x6, 0, 0, 0x00000001 }, 
}; 

struct sock_fprog bpf = { 
    .len = ARRAY_SIZE(code), 
    .filter = code, 
}; 

if (setsockopt(parentfd, SOL_SOCKET, SO_REUSEPORT, (const void *)&optval,sizeof(optval))) 
    error("ERROR setting SO_REUSEPORT"); 

if (setsockopt(parentfd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, (const void *)&bpf, sizeof(bpf))) 
    error("ERROR setting SO_ATTACH_REUSEPORT_CBPF); 

Tôi cũng có một quá trình khác nhau mà nghe vào cổng tương tự sử dụng chỉ cờ SO_REUSEPORT. Từ máy có IP 192.168.255.1 Tôi đang chạy echo 1234 | ncat 192.168.255.150 1234. Dựa trên bộ lọc của tôi, tôi dự kiến ​​tất cả lưu lượng truy cập từ địa chỉ IP đó sẽ được nhận bởi quy trình thứ hai. Tuy nhiên, nó là tất cả nhận được bởi người đầu tiên. Khi tôi thay đổi bộ lọc để một đơn giản:

struct sock_filter code[]={ { 0x6, 0, 0, 0x00000001 }, };

Nó hoạt động như mong đợi và tất cả các gói nhận được bởi quá trình thứ hai. Bất kỳ ý tưởng tại sao điều này có thể xảy ra?

+1

Làm thế nào bạn đã đưa ra các bộ lọc gốc ? –

Trả lời

1

Tôi đã tìm ra vấn đề là gì. Bộ lọc được áp dụng cho tất cả các gói, ngay cả các gói bắt tay TCP. Ngoài ra, con trỏ cơ sở trỏ đến byte đầu tiên của tải trọng gói, không phải tiêu đề. Do đó, khi nó thực thi

ldh[12] 

nó được ra khỏi giới hạn của gói tin (packet SYN có 0 byte payload) và hành vi mặc định là để trở về 0.

0

Mã không làm việc là:

l0: ldh [12]     /* read EtherType (2 bytes), which is found at offset 12 (decimal) */ 
l1: jeq #0x800, l2, l5   /* if EtherType == `0x800` (IPv4), jump to `l2`, otherwise jump to `l5` */ 
l2: ld [26]     /* read source IP address (4 bytes) */ 
l3: jeq #0xc0a8ff01, l6, l4 /* if source IP address == 192.168.255.1, jump to l6 (return 1), else jump to l4 (return 0) */ 
l4: ret #0 
l5: ret #0x40000 
l6: ret #0x1 

Mã làm việc là:

ret #0x1 

socket (7) nói:

Chương trình BPF phải trả lại một chỉ số giữa 0 và N -1 đại diện cho socket sẽ nhận được gói (trong đó N là số lượng ổ cắm trong nhóm). Nếu chương trình BPF trả về một chỉ mục không hợp lệ, việc lựa chọn socket sẽ quay trở lại cơ chế SO_REUSEPORT đơn giản.

Trên máy tính của tôi tcpdump -i lo -ddd 'src host 192.168.255.1' sản xuất

10 
40 0 0 12 
21 0 2 2048 
32 0 0 26 
21 4 5 3232300801 
21 1 0 2054 
21 0 3 32821 
32 0 0 28 
21 0 1 3232300801 
6 0 0 262144 
6 0 0 0 

Đó là

l0: ldh [12] 
l1: jeq #0x800, l2, l4 
l2: ld [26] 
l3: jeq #0xc0a8ff01, l8, l9 
l4: jeq #0x806, l6, l5 
l5: jeq #0x8035, l6, l9 
l6: ld [28] 
l7: jeq #0xc0a8ff01, l8, l9 
l8: ret #0x40000 
l9: ret #0 

tôi không thấy bất cứ điều gì đó là rõ ràng là sai với mã của bạn.

Bạn đã thử chạy tcpdump trên máy chủ chưa? Có lẽ bạn đã quên xóa một địa chỉ IP phụ trên máy khách, hoặc có một quy tắc SNAT bị lãng quên ở đâu đó?

Phiên bản hạt nhân nào bạn đang chạy? Bạn có thể đăng một ứng dụng C tối thiểu để tái tạo vấn đề?

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