I'm not a frequent nim user, but this is a feature I have been looking forwards 
to since my first nim project in 2016.

My usecase - I would like to compile bpf filters at compile time to attach to 
sockets, and I don't want to rely on libpcap being installed on target systems.

My code looks like this: 
    
    
    proc compile_filter*(filter: string): Filter {.compileTime.} =
      ## Gets a bpf string and uses libpcap to convert it to
      ## an array of bpf instructions.
      ##
      ## To compile the filter we use an external compiled (nim) program that
      ## uses pcap. In future versions of nim we will be able to use the
      ## ffi at compile time and then won't even need the external program
      
      let prog = gorge("../tools/compile_filter \"" & filter & "\"").split("\n")
      result = @[]
      
      try:
        for instruction in prog:
          let parts = instruction.split(" ")
          result.add(sock_filter(code: parts[0].parseInt.uint16,
                                 jt: parts[1].parseInt.uint8,
                                 jf: parts[2].parseInt.uint8,
                                 k: parts[3].parseInt.uint32))
      except:
        raise newException(InvalidFilterError, "Invalid filter " & filter)
    
    
    Run

and the important part of compile_filter minus types and boilerplate looks like 
this: 
    
    
    proc pcap_compile*(p: pcap_t; fp: ptr sock_fprog, str: cstring, optimize: 
int, netmask: int): cint {.importc, cdecl, header: "<pcap/pcap.h>".}
    
    when isMainModule:
      if paramCount() != 1:
        echo "Usage: " & paramStr(0) & " filter"
        quit(1)
      
      var program: sock_fprog
      let p* = pcap_open_dead(1, 65536)
      if p == nil:
        raiseOSError(osLastError())
      
      if pcap_compile(p, addr program, paramStr(1), 1, 0) != 0:
        raiseOSError(osLastError())
      for i in 0..program.len-1:
        echo program.filter[i]
      
      pcap_freecode(addr program)
    
    
    Run

This lets me write code like:
    
    
      let filter = compile_filter("udp port 1234")
      sock.attach_filter(filter)
    
    
    Run

The major advantage of having the FFI be available at compile time is not 
having to convert my data to a string in one program and then parsing the 
string again back into an object at compile time.

Reply via email to