Los lenguajes de programación de alto nivel son populares, pero hay áreas en las que necesitará utilizar implementaciones de bibliotecas no administradas. Esto puede ser una llamada a funciones específicas del sistema operativo, acceso de bajo nivel a dispositivos, la necesidad de rendimiento en algoritmos y otros. Debajo del corte, le diré lo que puede encontrar mientras viaja en código no administrado y lo que debe llevar consigo.
Estás parado en la puerta de tu acogedor IDE, y no tienes la tentación de ir al mundo del código fuente, donde está oscuro y nada claro. Para el éxito de la empresa, en primer lugar, debe obtener un mapa: aparecerá una descripción de los encabezados de la biblioteca y es mejor tener la documentación completa. Suele tener este aspecto:
...
#include <linux/netfilter_ipv4/ip_tables.h>
#include <libiptc/xtcshared.h>
#ifdef __cplusplus
extern "C" {
#endif
#define iptc_handle xtc_handle
#define ipt_chainlabel xt_chainlabel
#define IPTC_LABEL_ACCEPT "ACCEPT"
#define IPTC_LABEL_DROP "DROP"
#define IPTC_LABEL_QUEUE "QUEUE"
#define IPTC_LABEL_RETURN "RETURN"
/* Does this chain exist? */
int iptc_is_chain(const char *chain, struct xtc_handle *const handle);
/* Take a snapshot of the rules. Returns NULL on error. */
struct xtc_handle *iptc_init(const char *tablename);
/* Cleanup after iptc_init(). */
void iptc_free(struct xtc_handle *h);
...
Digamos que tiene suerte y la documentación está ahí. Describe las firmas de funciones, las estructuras utilizadas, los alias y las referencias a otros encabezados utilizados. La primera misión es encontrar la biblioteca en el sistema operativo. Su nombre puede diferir de lo esperado:
~$ find /usr/lib/x86_64-linux-gnu/ -maxdepth 1 -name 'libip*'
/usr/lib/x86_64-linux-gnu/libip6tc.so.0.1.0
/usr/lib/x86_64-linux-gnu/libip4tc.so
/usr/lib/x86_64-linux-gnu/libiptc.so.0
/usr/lib/x86_64-linux-gnu/libip4tc.so.0.1.0
/usr/lib/x86_64-linux-gnu/libip6tc.so.0
/usr/lib/x86_64-linux-gnu/libiptc.so.0.0.0
/usr/lib/x86_64-linux-gnu/libip4tc.so.0
/usr/lib/x86_64-linux-gnu/libiptc.so
/usr/lib/x86_64-linux-gnu/libip6tc.so
Sufijo numérico significa diferentes versiones de bibliotecas. En general, necesitamos el libip4tc.so original. Puedes mirar adentro con un ojo y asegurarte de que vale la pena:
~$ nm -D /usr/lib/x86_64-linux-gnu/libip4tc.so ... 0000000000206230 D _edata 0000000000206240 B _end U __errno_location U fcntl 000000000000464c T _fini U __fprintf_chk U free U getsockopt w __gmon_start__ 0000000000001440 T _init 0000000000003c80 T iptc_append_entry 0000000000003700 T iptc_builtin 0000000000004640 T iptc_check_entry 0000000000003100 T iptc_commit 0000000000002ff0 T iptc_create_chain 00000000000043f0 T iptc_delete_chain ...
, , , . :
public static class Libiptc4
{
/* Prototype: iptc_handle_t iptc_init(const char *tablename) */
[DllImport("libip4tc.so")]
public static extern IntPtr iptc_init(string tablename);
}
/* Prototype: iptc_handle_t iptc_init(const char *tablename) */
[DllImport("libip4tc.so")]
public static extern IntPtr iptc_init(IntPtr tblPtr);
...
var tblPtr = Marshal.StringToHGlobalAnsi("filter");
var _handle = Libiptc4.iptc_init_ptr(tblPtr);
Marshal.FreeHGlobal(tblPtr);
.
, , , , .
, : . :
struct ipt_entry {
struct ipt_ip ip;
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
__u16 target_offset;
/* Size of ipt_entry + matches + target */
__u16 next_offset;
/* Back pointer */
unsigned int comefrom;
/* Packet and byte counters. */
struct xt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
unsigned char elems[0]
. , , . :
*******************************************
* ip_entry *
* 112 bytes *
*******************************************
* matches *
* target_offset - 112 bytes *
*******************************************
* target *
* next_offset - target_offset - 112 bytes *
*******************************************
(matches
target
) ip_entry
. :
.
.
, , . ipt_entry
:
[StructLayout(LayoutKind.Sequential)]
public struct IptEntry
{
public IptIp ip;
public uint nfcache;
public ushort target_offset;
public ushort next_offset;
public uint comefrom;
public IptCounters counters;
};
Marshal.SizeOf<IptEntry>()
112 . matches
target
( ). : libiptc
8 ( long
), . , . :
static readonly int _WORDLEN = Marshal.SizeOf<long>();
public static int Align(int size)
{
return ((size + (_WORDLEN - 1)) & ~(_WORDLEN - 1));
}
, entry.target_offset
entry.next_offset
, :
IntPtr entryPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr<IptEntry>(entryPtr, entry, false);
Marshal.StructureToPtr<Match>(entryPtr + 112, match, false);
: , , :
var entry = Marshal.PtrToStructure<IptEntry>(point);
var match = Marshal.PtrToStructure<Match>(point + 112)
, union:
struct xt_entry_match {
union {
struct {
__u16 match_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN];
__u8 revision;
} user;
struct {
__u16 match_size;
/* Used inside the kernel */
struct xt_match *match;
} kernel;
/* Total length */
__u16 match_size;
} u;
unsigned char data[0];
};
:
#define XT_EXTENSION_MAXNAMELEN 29
...
char name [XT_EXTENSION_MAXNAMELEN]
. header .
, . ushort, uint long , . . : , . . . :
byte [] convArray = BitConverter.GetBytes(value);
Array.Reverse(convArray);
ushort reverseEndian = BitConverter.ToUInt16(convArray,0);
ushort reverseEndian = (ushort)((value << 8) | (value >> 8));
. unmanaged code . /, errno. . , :
[DllImport("libip4tc.so", SetLastError = true)]
, , :
int errno = Marshal.GetLastWin32Error();
var errPtr = Libiptc4.iptc_strerror(errno);
string errStr = Marshal.PtrToStringAnsi(errPtr);
Linux c net.core (, /). : -, 32/64 , Windows . .
Con esto concluye nuestro viaje. Agregaré que el uso de bibliotecas de bajo nivel parece ser un enfoque difícil e impredecible en términos de éxito. La falta de documentación y las interacciones complejas pueden ser aterradoras. Sin embargo, a veces esta es la única forma de lograr el resultado deseado.