RTL8139.lua (7847B)
1 #!/usr/bin/env luajit 2 -- RTL8139 Network Driver Library 3 -- Provides high-level network interface for RTL8139 network card 4 5 local RTL8139 = {} 6 7 -- Driver state 8 RTL8139.initialized = false 9 RTL8139.mac_address = nil 10 RTL8139.rx_callbacks = {} -- Packet receive callbacks 11 12 ---Initialize the RTL8139 network card 13 ---@return boolean success True if initialization succeeded 14 function RTL8139.init() 15 if osprint then 16 osprint("RTL8139.lua: Initializing network driver...\n") 17 end 18 19 -- Call C function to initialize hardware 20 if not RTL8139Init then 21 if osprint then 22 osprint("RTL8139.lua: ERROR - RTL8139Init function not available\n") 23 end 24 return false 25 end 26 27 local success = RTL8139Init() 28 29 if success then 30 RTL8139.initialized = true 31 32 -- Get MAC address 33 RTL8139.mac_address = RTL8139GetMAC() 34 35 if osprint then 36 osprint("RTL8139.lua: Initialized successfully\n") 37 if RTL8139.mac_address then 38 osprint("RTL8139.lua: MAC Address: ") 39 for i = 1, 6 do 40 osprint(string.format("%02X", RTL8139.mac_address[i])) 41 if i < 6 then osprint(":") end 42 end 43 osprint("\n") 44 end 45 end 46 else 47 if osprint then 48 osprint("RTL8139.lua: Initialization failed\n") 49 end 50 end 51 52 return success 53 end 54 55 ---Get the MAC address of the network card 56 ---@return table|nil mac Array of 6 bytes, or nil if not initialized 57 function RTL8139.getMACAddress() 58 if not RTL8139.initialized then 59 return nil 60 end 61 62 return RTL8139.mac_address 63 end 64 65 ---Get MAC address as a formatted string 66 ---@return string|nil macString "XX:XX:XX:XX:XX:XX" format 67 function RTL8139.getMACString() 68 if not RTL8139.mac_address then 69 return nil 70 end 71 72 local parts = {} 73 for i = 1, 6 do 74 parts[i] = string.format("%02X", RTL8139.mac_address[i]) 75 end 76 return table.concat(parts, ":") 77 end 78 79 ---Send a raw Ethernet packet 80 ---@param data string Raw packet data (including Ethernet header) 81 ---@return number bytesent Number of bytes sent, or -1 on error 82 function RTL8139.send(data) 83 if not RTL8139.initialized then 84 return -1 85 end 86 87 if type(data) ~= "string" then 88 return -1 89 end 90 91 return RTL8139Send(data) 92 end 93 94 ---Receive a raw Ethernet packet (non-blocking) 95 ---@return string|nil packet Raw packet data, or nil if no packet available 96 function RTL8139.receive() 97 if not RTL8139.initialized then 98 return nil 99 end 100 101 return RTL8139Receive() 102 end 103 104 ---Register a callback for received packets 105 ---@param callback function Function to call when packet is received 106 ---@param filter table|nil Optional filter {ethertype = 0x0800} to filter packets 107 ---@return number id Callback ID for later removal 108 function RTL8139.onReceive(callback, filter) 109 local id = #RTL8139.rx_callbacks + 1 110 RTL8139.rx_callbacks[id] = { 111 callback = callback, 112 filter = filter 113 } 114 return id 115 end 116 117 ---Remove a receive callback 118 ---@param id number Callback ID returned by onReceive 119 function RTL8139.removeReceiveCallback(id) 120 RTL8139.rx_callbacks[id] = nil 121 end 122 123 ---Poll for received packets and call callbacks 124 ---Should be called regularly from main loop 125 ---@return number count Number of packets processed 126 function RTL8139.poll() 127 if not RTL8139.initialized then 128 return 0 129 end 130 131 local count = 0 132 133 -- Process all available packets 134 while true do 135 local packet = RTL8139.receive() 136 if not packet then 137 break 138 end 139 140 count = count + 1 141 142 -- Parse Ethernet header 143 local eth = RTL8139.parseEthernetHeader(packet) 144 145 -- Call registered callbacks 146 for id, cb in pairs(RTL8139.rx_callbacks) do 147 local should_call = true 148 149 -- Apply filter if present 150 if cb.filter then 151 if cb.filter.ethertype and eth.ethertype ~= cb.filter.ethertype then 152 should_call = false 153 end 154 end 155 156 if should_call then 157 local success, err = pcall(cb.callback, packet, eth) 158 if not success and osprint then 159 osprint("RTL8139.lua: Callback error: " .. tostring(err) .. "\n") 160 end 161 end 162 end 163 end 164 165 return count 166 end 167 168 ---Parse Ethernet header from packet 169 ---@param packet string Raw packet data 170 ---@return table header Ethernet header fields 171 function RTL8139.parseEthernetHeader(packet) 172 if #packet < 14 then 173 return nil 174 end 175 176 local header = {} 177 178 -- Destination MAC (6 bytes) 179 header.dst_mac = {} 180 for i = 1, 6 do 181 header.dst_mac[i] = string.byte(packet, i) 182 end 183 184 -- Source MAC (6 bytes) 185 header.src_mac = {} 186 for i = 1, 6 do 187 header.src_mac[i] = string.byte(packet, 6 + i) 188 end 189 190 -- EtherType (2 bytes, big endian) 191 local b1, b2 = string.byte(packet, 13, 14) 192 header.ethertype = (b1 * 256) + b2 193 194 -- Payload starts at byte 15 195 header.payload = string.sub(packet, 15) 196 197 return header 198 end 199 200 ---Build an Ethernet frame 201 ---@param dst_mac table Destination MAC address (6 bytes) 202 ---@param src_mac table Source MAC address (6 bytes) 203 ---@param ethertype number EtherType (0x0800 for IPv4, 0x0806 for ARP, etc.) 204 ---@param payload string Payload data 205 ---@return string frame Complete Ethernet frame 206 function RTL8139.buildEthernetFrame(dst_mac, src_mac, ethertype, payload) 207 local frame = {} 208 209 -- Destination MAC (6 bytes) 210 for i = 1, 6 do 211 frame[#frame + 1] = string.char(dst_mac[i]) 212 end 213 214 -- Source MAC (6 bytes) 215 for i = 1, 6 do 216 frame[#frame + 1] = string.char(src_mac[i]) 217 end 218 219 -- EtherType (2 bytes, big endian) 220 frame[#frame + 1] = string.char(math.floor(ethertype / 256)) 221 frame[#frame + 1] = string.char(ethertype % 256) 222 223 -- Payload 224 frame[#frame + 1] = payload 225 226 return table.concat(frame) 227 end 228 229 ---Send an Ethernet frame with automatic source MAC 230 ---@param dst_mac table Destination MAC address 231 ---@param ethertype number EtherType 232 ---@param payload string Payload data 233 ---@return number bytessent Number of bytes sent 234 function RTL8139.sendFrame(dst_mac, ethertype, payload) 235 if not RTL8139.initialized or not RTL8139.mac_address then 236 return -1 237 end 238 239 local frame = RTL8139.buildEthernetFrame(dst_mac, RTL8139.mac_address, ethertype, payload) 240 return RTL8139.send(frame) 241 end 242 243 ---Send a broadcast packet 244 ---@param ethertype number EtherType 245 ---@param payload string Payload data 246 ---@return number bytessent Number of bytes sent 247 function RTL8139.sendBroadcast(ethertype, payload) 248 local broadcast_mac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 249 return RTL8139.sendFrame(broadcast_mac, ethertype, payload) 250 end 251 252 ---Common EtherType constants 253 RTL8139.ETHERTYPE = { 254 IPV4 = 0x0800, 255 ARP = 0x0806, 256 IPV6 = 0x86DD, 257 VLAN = 0x8100, 258 } 259 260 ---Format MAC address for display 261 ---@param mac table MAC address (6 bytes) 262 ---@return string formatted "XX:XX:XX:XX:XX:XX" 263 function RTL8139.formatMAC(mac) 264 if not mac or #mac ~= 6 then 265 return "00:00:00:00:00:00" 266 end 267 268 local parts = {} 269 for i = 1, 6 do 270 parts[i] = string.format("%02X", mac[i]) 271 end 272 return table.concat(parts, ":") 273 end 274 275 ---Parse MAC address from string 276 ---@param str string "XX:XX:XX:XX:XX:XX" format 277 ---@return table|nil mac Array of 6 bytes 278 function RTL8139.parseMAC(str) 279 local mac = {} 280 for byte_str in string.gmatch(str, "[^:]+") do 281 local byte_val = tonumber(byte_str, 16) 282 if not byte_val then 283 return nil 284 end 285 mac[#mac + 1] = byte_val 286 end 287 288 if #mac ~= 6 then 289 return nil 290 end 291 292 return mac 293 end 294 295 -- Export module 296 return RTL8139