Skip to content

Commit e1f2b22

Browse files
committed
feat(socket): add support raw socket
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
1 parent a626b0e commit e1f2b22

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

examples/socket/raw/arping.lua

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env eco
2+
3+
local hex = require 'eco.encoding.hex'
4+
local socket = require 'eco.socket'
5+
local link = require 'eco.ip'.link
6+
local sys = require 'eco.sys'
7+
8+
if #arg < 2 then
9+
print('Usage:', arg[0], 'device', 'destination')
10+
os.exit(1)
11+
end
12+
13+
sys.signal(sys.SIGINT, function()
14+
print('\nGot SIGINT, now quit')
15+
eco.unloop()
16+
end)
17+
18+
local sock, err = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(socket.ETH_P_ARP))
19+
if not sock then
20+
error(err)
21+
end
22+
23+
local function send_arp(device, destination)
24+
local res, err = link.get(device)
25+
if not res then
26+
error('get link:' .. err)
27+
end
28+
29+
local sender_mac = res.address
30+
31+
print(sender_mac .. ' > ff:ff:ff:ff:ff:ff ARP REQUEST ' .. destination)
32+
33+
sender_mac = hex.decode(sender_mac:gsub(':', ''))
34+
35+
local data = {
36+
string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff), -- dest broadcast
37+
sender_mac,
38+
string.pack('>I2', socket.ETH_P_ARP),
39+
string.pack('>I2', socket.ARPHRD_ETHER),
40+
string.pack('>I2', socket.ETH_P_IP),
41+
string.char(6), -- hardware address length
42+
string.char(4), -- proto address length
43+
string.pack('>I2', socket.ARPOP_REQUEST),
44+
sender_mac,
45+
string.pack('>I4', 0), -- ender_ip
46+
'\0\0\0\0\0\0', -- target MAC address
47+
string.pack('I4', socket.inet_aton(destination)),
48+
}
49+
50+
sock:sendto(table.concat(data), { ifname = device })
51+
end
52+
53+
local function packet_dump(data)
54+
local dst = hex.encode(data:sub(1, 6), ':')
55+
local src = hex.encode(data:sub(7, 12), ':')
56+
local proto = string.unpack('>I2', data:sub(13, 14))
57+
58+
if proto ~= socket.ETH_P_ARP then
59+
return
60+
end
61+
62+
local output = {src, '>', dst}
63+
64+
data = data:sub(15) -- skip mac header
65+
66+
if proto == socket.ETH_P_ARP then
67+
output[#output + 1] = 'ARP'
68+
end
69+
70+
local op = string.unpack('>I2', data:sub(7))
71+
72+
output[#output + 1] = op == socket.ARPOP_REQUEST and 'REQUEST' or 'REPLY'
73+
74+
if op ~= socket.ARPOP_REPLY then
75+
print(op)
76+
return
77+
end
78+
79+
output[#output + 1] = socket.inet_ntop(socket.AF_INET, data:sub(15))
80+
output[#output + 1] = hex.encode(data:sub(9, 14), ':')
81+
82+
print(table.concat(output, ' '))
83+
end
84+
85+
local device = arg[1]
86+
local destination = arg[2]
87+
88+
send_arp(device, destination)
89+
90+
local data = sock:recv(1024)
91+
92+
packet_dump(data)

examples/socket/raw/capture.lua

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env eco
2+
3+
local hex = require 'eco.encoding.hex'
4+
local socket = require 'eco.socket'
5+
local sys = require 'eco.sys'
6+
7+
sys.signal(sys.SIGINT, function()
8+
print('\nGot SIGINT, now quit')
9+
eco.unloop()
10+
end)
11+
12+
local sock, err = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(socket.ETH_P_ALL))
13+
if not sock then
14+
error(err)
15+
end
16+
17+
sock:setoption('bindtodevice', 'br-lan')
18+
19+
local function packet_parse(data, proto, output)
20+
if proto == socket.ETH_P_8021Q then
21+
output[#output + 1] = '8021Q'
22+
23+
local tci = string.unpack('>I2', data:sub(1))
24+
output[#output + 1] = 'pri ' .. (tci >> 13)
25+
output[#output + 1] = 'vid ' .. (tci & 0xfff)
26+
27+
proto = string.unpack('>I2', data:sub(3))
28+
29+
return packet_parse(data:sub(5), proto, output)
30+
elseif proto == socket.ETH_P_ARP then
31+
output[#output + 1] = 'ARP'
32+
33+
local op = string.unpack('>I2', data:sub(7))
34+
35+
output[#output + 1] = op == socket.ARPOP_REQUEST and 'REQUEST' or 'REPLY'
36+
37+
if op == socket.ARPOP_REQUEST then
38+
output[#output + 1] = socket.inet_ntop(socket.AF_INET, data:sub(25))
39+
else
40+
output[#output + 1] = socket.inet_ntop(socket.AF_INET, data:sub(15))
41+
output[#output + 1] = hex.encode(data:sub(9, 14), ':')
42+
end
43+
elseif proto == socket.ETH_P_IP then
44+
output[#output + 1] = 'IP'
45+
46+
local dst_ip = socket.inet_ntop(socket.AF_INET, data:sub(13))
47+
local src_ip = socket.inet_ntop(socket.AF_INET, data:sub(17))
48+
49+
output[#output + 1] = src_ip
50+
output[#output + 1] = '>'
51+
output[#output + 1] = dst_ip
52+
53+
local iphl = (data:byte(1) & 0xf) * 4
54+
55+
local ip_proto = data:byte(10)
56+
if ip_proto == socket.IPPROTO_ICMP then
57+
output[#output + 1] = 'ICMP'
58+
elseif ip_proto == socket.IPPROTO_TCP or ip_proto == socket.IPPROTO_UDP then
59+
output[#output + 1] = ip_proto == socket.IPPROTO_TCP and 'TCP' or 'UDP'
60+
61+
data = data:sub(iphl + 1)
62+
local src_port = string.unpack('>I2', data)
63+
local dst_port = string.unpack('>I2', data:sub(3))
64+
65+
output[#output + 1] = src_port
66+
output[#output + 1] = '>'
67+
output[#output + 1] = dst_port
68+
end
69+
end
70+
71+
print(table.concat(output, ' '))
72+
end
73+
74+
while true do
75+
local data, err = sock:recv(4096)
76+
if not data then
77+
error(err)
78+
end
79+
80+
local dst = hex.encode(data:sub(1, 6), ':')
81+
local src = hex.encode(data:sub(7, 12), ':')
82+
local proto = string.unpack('>I2', data:sub(13))
83+
84+
if proto ~= socket.ETH_P_8021Q and proto ~= socket.ETH_P_ARP and proto ~= socket.ETH_P_IP then
85+
return
86+
end
87+
88+
local output = {src, '>', dst}
89+
90+
data = data:sub(15) -- skip mac header
91+
92+
packet_parse(data, proto, output)
93+
94+
print(table.concat(output, ' '))
95+
end

socket.c

+37
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <sys/un.h>
1717

1818
#include <linux/netlink.h>
19+
#include <linux/if_ether.h>
20+
#include <linux/if_arp.h>
1921
#include <netinet/tcp.h>
2022

2123
#include "eco.h"
@@ -153,6 +155,7 @@ static int lua_args_to_sockaddr(struct eco_socket *sock, lua_State *L, struct so
153155
struct sockaddr_un un;
154156
struct sockaddr_in in;
155157
struct sockaddr_in6 in6;
158+
struct sockaddr_ll ll;
156159
} addr = {
157160
.a.sa_family = sock->domain
158161
};
@@ -194,6 +197,24 @@ static int lua_args_to_sockaddr(struct eco_socket *sock, lua_State *L, struct so
194197
addr.nl.nl_pid = luaL_optinteger(L, 3 + offset, 0);
195198
break;
196199

200+
case AF_PACKET:
201+
luaL_checktype(L, 2 + offset, LUA_TTABLE);
202+
203+
addrlen = sizeof(struct sockaddr_ll);
204+
205+
lua_getfield(L, 2 + offset, "ifindex");
206+
if (!lua_isnil(L, -1))
207+
addr.ll.sll_ifindex = luaL_checkinteger(L, -1);
208+
lua_pop(L, 1);
209+
210+
lua_getfield(L, 2 + offset, "ifname");
211+
if (!lua_isnil(L, -1)) {
212+
const char *ifname = luaL_checkstring(L, -1);
213+
addr.ll.sll_ifindex = if_nametoindex(ifname);
214+
}
215+
lua_pop(L, 1);
216+
break;
217+
197218
default:
198219
return luaL_error(L, "invalid domain");
199220
}
@@ -1041,5 +1062,21 @@ int luaopen_eco_core_socket(lua_State *L)
10411062
lua_add_constant(L, "IPPROTO_ICMP", IPPROTO_ICMP);
10421063
lua_add_constant(L, "IPPROTO_ICMPV6", IPPROTO_ICMPV6);
10431064

1065+
lua_add_constant(L, "IPPROTO_TCP", IPPROTO_TCP);
1066+
lua_add_constant(L, "IPPROTO_UDP", IPPROTO_UDP);
1067+
1068+
lua_add_constant(L, "ETH_P_IP", ETH_P_IP);
1069+
lua_add_constant(L, "ETH_P_ARP", ETH_P_ARP);
1070+
lua_add_constant(L, "ETH_P_8021Q", ETH_P_8021Q);
1071+
lua_add_constant(L, "ETH_P_PPP_DISC", ETH_P_PPP_DISC);
1072+
lua_add_constant(L, "ETH_P_PPP_SES", ETH_P_PPP_SES);
1073+
lua_add_constant(L, "ETH_P_IPV6", ETH_P_IPV6);
1074+
lua_add_constant(L, "ETH_P_ALL", ETH_P_ALL);
1075+
1076+
lua_add_constant(L, "ARPHRD_ETHER", ARPHRD_ETHER);
1077+
1078+
lua_add_constant(L, "ARPOP_REQUEST", ARPOP_REQUEST);
1079+
lua_add_constant(L, "ARPOP_REPLY", ARPOP_REPLY);
1080+
10441081
return 1;
10451082
}

0 commit comments

Comments
 (0)