From 114a9e1da3cc4399fd1de28d752610a8333e060a Mon Sep 17 00:00:00 2001
From: Martin Teichmann <martin.teichmann@gmail.com>
Date: Wed, 15 Feb 2023 08:51:38 +0000
Subject: [PATCH] supported fixed point numbers

---
 ebpfcat/arraymap.py  |  14 ++-
 ebpfcat/ebpf.py      | 232 +++++++++++++++++++++++++++++++++----------
 ebpfcat/ebpf_test.py | 173 ++++++++++++++++++++++++++++++--
 ebpfcat/hashmap.py   |   1 +
 4 files changed, 353 insertions(+), 67 deletions(-)

diff --git a/ebpfcat/arraymap.py b/ebpfcat/arraymap.py
index b20934c..05fadec 100644
--- a/ebpfcat/arraymap.py
+++ b/ebpfcat/arraymap.py
@@ -19,7 +19,7 @@ from itertools import chain
 from mmap import mmap
 from struct import pack_into, unpack_from, calcsize
 
-from .ebpf import FuncId, Map, MemoryDesc, Opcode, SubProgram
+from .ebpf import Expression, FuncId, Map, MemoryDesc, Opcode, SubProgram
 from .bpf import create_map, lookup_elem, MapType, MapFlags, update_elem
 
 
@@ -29,6 +29,7 @@ class ArrayGlobalVarDesc(MemoryDesc):
     def __init__(self, map, fmt):
         self.map = map
         self.fmt = fmt
+        self.fixed = fmt == "f"
 
     def fmt_addr(self, ebpf):
         return self.fmt, ebpf.__dict__[self.name]
@@ -42,7 +43,10 @@ class ArrayGlobalVarDesc(MemoryDesc):
         if instance.ebpf.loaded:
             fmt, addr = self.fmt_addr(instance)
             data = instance.ebpf.__dict__[self.map.name].data
-            ret = unpack_from(fmt, data, addr)
+            if fmt == "f":
+                return unpack_from("q", data, addr)[0] / Expression.FIXED_BASE
+            else:
+                ret = unpack_from(fmt, data, addr)
             if len(ret) == 1:
                 return ret[0]
             else:
@@ -53,6 +57,9 @@ class ArrayGlobalVarDesc(MemoryDesc):
     def __set__(self, instance, value):
         if instance.ebpf.loaded:
             fmt, addr = self.fmt_addr(instance)
+            if fmt == "f":
+                fmt = "q"
+                value = int(value * Expression.FIXED_BASE)
             if not isinstance(value, tuple):
                 value = value,
             pack_into(fmt, instance.ebpf.__dict__[self.map.name].data,
@@ -80,7 +87,8 @@ class ArrayMap(Map):
         for prog in chain([ebpf], ebpf.subprograms):
             for k, v in prog.__class__.__dict__.items():
                 if isinstance(v, ArrayGlobalVarDesc):
-                    collection.append((calcsize(v.fmt), prog, k))
+                    collection.append((8 if v.fmt == "f" else calcsize(v.fmt),
+                                       prog, k))
         collection.sort(key=lambda t: t[0], reverse=True)
         position = 0
         for size, prog, name in collection:
diff --git a/ebpfcat/ebpf.py b/ebpfcat/ebpf.py
index 39abe09..0750830 100644
--- a/ebpfcat/ebpf.py
+++ b/ebpfcat/ebpf.py
@@ -18,6 +18,7 @@
 from abc import ABC, abstractmethod
 from collections import namedtuple
 from contextlib import contextmanager, ExitStack
+from operator import index
 from struct import pack, unpack, calcsize
 from enum import Enum
 
@@ -268,10 +269,18 @@ class AssembleError(Exception):
 
 def comparison(uposop, unegop, sposop, snegop):
     def ret(self, value):
+        valuefixed, value = fixedvalue(value)
+        myself = self
+        if self.fixed != valuefixed:
+            if self.fixed:
+                value = value * self.FIXED_BASE
+            else:
+                myself = self * self.FIXED_BASE
+
         if self.signed or issigned(value):
-            return SimpleComparison(self.ebpf, self, value, (sposop, snegop))
+            return SimpleComparison(self.ebpf, myself, value, (sposop, snegop))
         else:
-            return SimpleComparison(self.ebpf, self, value, (uposop, unegop))
+            return SimpleComparison(self.ebpf, myself, value, (uposop, unegop))
     return ret
 
 
@@ -429,39 +438,112 @@ def issigned(value):
         return value < 0
 
 
-def binary(opcode):
-    def ret(self, value):
-        return Binary(self.ebpf, self, value, opcode,
-                      self.signed or issigned(value), False)
-    return ret
-
-def rbinary(opcode):
-    def ret(self, value):
-        return ReverseBinary(self.ebpf, value, self, opcode, value < 0, False)
-    return ret
+def fixedvalue(value):
+    try:
+        return False, index(value)
+    except TypeError:
+        try:
+            return True, int(float(value) * Expression.FIXED_BASE)
+        except TypeError:
+            return value.fixed, value
 
 
 class Expression:
     """the base class for all numerical expressions"""
-    __radd__ = __add__ = binary(Opcode.ADD)
-    __sub__ = binary(Opcode.SUB)
-    __rsub__ = rbinary(Opcode.SUB)
-    __rmul__ = __mul__ = binary(Opcode.MUL)
-    __floordiv__ = binary(Opcode.DIV)
-    __rfloordiv__ = rbinary(Opcode.DIV)
-    __ror__ = __or__ = binary(Opcode.OR)
-    __lshift__ = binary(Opcode.LSH)
-    __rlshift__ = rbinary(Opcode.LSH)
-    __rrshift__ = rbinary(Opcode.RSH)
-    __mod__ = binary(Opcode.MOD)
-    __rmod__ = rbinary(Opcode.MOD)
-    __rxor__ = __xor__ = binary(Opcode.XOR)
+
+    FIXED_BASE = 100000
+
+    def _binary(self, value, opcode):
+        return Binary(self.ebpf, self, value, opcode,
+                      self.signed or issigned(value), False)
+
+    __ror__ = __or__ = lambda self, value: self._binary(value, Opcode.OR)
+    __lshift__ = lambda self, value: self._binary(value, Opcode.LSH)
+    __rxor__ = __xor__ = lambda self, value: self._binary(value, Opcode.XOR)
 
     __gt__ = comparison(Opcode.JGT, Opcode.JLE, Opcode.JSGT, Opcode.JSLE)
     __ge__ = comparison(Opcode.JGE, Opcode.JLT, Opcode.JSGE, Opcode.JSLT)
     __lt__ = comparison(Opcode.JLT, Opcode.JGE, Opcode.JSLT, Opcode.JSGE)
     __le__ = comparison(Opcode.JLE, Opcode.JGT, Opcode.JSLE, Opcode.JSGT)
 
+    def _sum(self, value, opcode):
+        valuefixed, value = fixedvalue(value)
+        myself = self
+        if self.fixed != valuefixed:
+            if self.fixed:
+                value = value * self.FIXED_BASE
+            else:
+                myself = self * self.FIXED_BASE
+
+        return Binary(self.ebpf, myself, value, opcode,
+                      self.signed or issigned(value), self.fixed or valuefixed)
+
+    def _rsum(self, value, opcode):
+        valuefixed, value = fixedvalue(value)
+        myself = self
+        if self.fixed != valuefixed:
+            if self.fixed:
+                value = value * self.FIXED_BASE
+            else:
+                myself = self * self.FIXED_BASE
+
+        return ReverseBinary(
+            self.ebpf, value, myself, opcode,
+            self.signed or issigned(value), self.fixed or valuefixed)
+
+    __radd__ = __add__ = lambda self, value: self._sum(value, Opcode.ADD)
+    __sub__ = lambda self, value: self._sum(value, Opcode.SUB)
+    __rsub__ = lambda self, value: self._rsum(value, Opcode.SUB)
+    __mod__ = lambda self, value: self._sum(value, Opcode.MOD)
+    __rmod__ = lambda self, value: self._rsum(value, Opcode.MOD)
+
+    def __mul__(self, value):
+        valuefixed, value = fixedvalue(value)
+        ret = Binary(self.ebpf, self, value, Opcode.MUL,
+                     self.signed or issigned(value), self.fixed or valuefixed)
+        if self.fixed and valuefixed:
+            ret = ret / self.FIXED_BASE
+        return ret
+    __rmul__ = __mul__
+
+    def __truediv__(self, value):
+        valuefixed, value = fixedvalue(value)
+        myself = self
+        if not self.fixed and valuefixed:
+            myself = myself * self.FIXED_BASE ** 2
+        elif self.fixed == valuefixed:
+            myself = myself * self.FIXED_BASE
+
+        return Binary(self.ebpf, myself, value, Opcode.DIV,
+                      self.signed or issigned(value), True)
+
+    def __rtruediv__(self, value):
+        if self.fixed:
+            value = int(value * self.FIXED_BASE ** 2)
+        else:
+            value = int(value * self.FIXED_BASE)
+        return ReverseBinary(self.ebpf, value, self, Opcode.DIV,
+                             self.signed or issigned(value), True)
+
+    def __floordiv__(self, value):
+        valuefixed, value = fixedvalue(value)
+        myself = self
+        if not self.fixed and valuefixed:
+            myself = myself * self.FIXED_BASE
+        elif self.fixed and not valuefixed:
+            value = value * self.FIXED_BASE
+
+        return Binary(self.ebpf, myself, value, Opcode.DIV,
+                      self.signed or issigned(value), False)
+
+    def __rfloordiv__(self, value):
+        if self.fixed:
+            value = int(value * self.FIXED_BASE)
+        else:
+            value = int(value)
+        return ReverseBinary(self.ebpf, value, self, Opcode.DIV,
+                             self.signed or issigned(value), False)
+
     def __rshift__(self, value):
         opcode = Opcode.ARSH if self.signed else Opcode.RSH
         return Binary(self.ebpf, self, value, opcode, self.signed, False)
@@ -470,13 +552,16 @@ class Expression:
         opcode = Opcode.ARSH if value < 0 else Opcode.RSH
         return ReverseBinary(self.ebpf, value, self, opcode, value < 0, False)
 
+    def __rlshift__(self, value):
+        return ReverseBinary(self.ebpf, value, self, Opcode.LSH,
+                             value < 0, False)
+
     def __and__(self, value):
         return AndExpression(self.ebpf, self, value)
 
     def __ne__(self, value):
-        return SimpleComparison(
-            self.ebpf, self, value,
-            (Opcode.JNE, Opcode.JEQ, Opcode.JNE, Opcode.JEQ))
+        return SimpleComparison(self.ebpf, self, value,
+                                (Opcode.JNE, Opcode.JEQ))
 
     def __eq__(self, value):
         return ~(self != value)
@@ -626,6 +711,7 @@ class Negate(Expression):
         self.ebpf = ebpf
         self.arg = arg
         self.signed = True
+        self.fixed = arg.fixed
 
     @contextmanager
     def calculate(self, dst, long, force=False):
@@ -641,12 +727,13 @@ class Absolute(Expression):
     def __init__(self, ebpf, arg):
         self.ebpf = ebpf
         self.arg = arg
+        self.fixed = arg.fixed
 
     @contextmanager
     def calculate(self, dst, long, force=False):
         with self.arg.calculate(dst, long, force) as (dst, long):
-            with self.ebpf.r[dst] < 0:
-                self.ebpf.r[dst] = -self.ebpf.r[dst]
+            with self.ebpf.sr[dst] < 0:
+                self.ebpf.sr[dst] = -self.ebpf.sr[dst]
             yield dst, long
 
     def contains(self, no):
@@ -662,18 +749,18 @@ class Sum(Binary):
         super().__init__(ebpf, left, right, Opcode.ADD, right < 0, False)
 
     def __add__(self, value):
-        if isinstance(value, Expression):
+        try:
+            return Sum(self.ebpf, self.left, self.right + index(value))
+        except TypeError:
             return super().__add__(value)
-        else:
-            return Sum(self.ebpf, self.left, self.right + value)
 
     __radd__ = __add__
 
     def __sub__(self, value):
-        if isinstance(value, Expression):
-            return super().__sub__(value)
-        else:
-            return Sum(self.ebpf, self.left, self.right - value)
+        try:
+            return Sum(self.ebpf, self.left, self.right - index(value))
+        except TypeError:
+            return super().__add__(value)
 
 
 class AndExpression(Binary):
@@ -733,25 +820,30 @@ class Register(Expression):
     """represent one EBPF register"""
     offset = 0
 
-    def __init__(self, no, ebpf, long, signed):
+    def __init__(self, no, ebpf, long, signed, fixed=False):
         self.no = no
         self.ebpf = ebpf
         self.long = long
         self.signed = signed
+        self.fixed = fixed
 
     def __add__(self, value):
-        if isinstance(value, Expression) or not self.long:
-            return super().__add__(value)
-        else:
-            return Sum(self.ebpf, self, value)
+        if self.long and not self.fixed:
+            try:
+                return Sum(self.ebpf, self, index(value))
+            except TypeError:
+                pass
+        return super().__add__(value)
 
     __radd__ = __add__
 
     def __sub__(self, value):
-        if isinstance(value, Expression) or not self.long:
-            return super().__sub__(value)
-        else:
-            return Sum(self.ebpf, self, -value)
+        if self.long and not self.fixed:
+            try:
+                return Sum(self.ebpf, self, -index(value))
+            except TypeError:
+                pass
+        return super().__sub__(value)
 
     @contextmanager
     def calculate(self, dst, long, force=False):
@@ -778,7 +870,7 @@ class Memory(Expression):
     bits_to_opcode = {32: Opcode.W, 16: Opcode.H, 8: Opcode.B, 64: Opcode.DW}
     fmt_to_opcode = {'I': Opcode.W, 'H': Opcode.H, 'B': Opcode.B, 'Q': Opcode.DW,
                      'i': Opcode.W, 'h': Opcode.H, 'b': Opcode.B, 'q': Opcode.DW,
-                     'A': Opcode.W}
+                     'A': Opcode.W, 'f': Opcode.DW}
 
     def __init__(self, ebpf, fmt, address):
         self.ebpf = ebpf
@@ -828,6 +920,10 @@ class Memory(Expression):
     def signed(self):
         return isinstance(self.fmt, str) and self.fmt.islower()
 
+    @property
+    def fixed(self):
+        return isinstance(self.fmt, str) and self.fmt == "f"
+
     def __invert__(self):
         if not isinstance(self.fmt, tuple) or self.fmt[1] != 1:
             return NotImplemented
@@ -880,6 +976,8 @@ class MemoryDesc:
         elif isinstance(value, IAdd):
             value = value.value
             if not isinstance(value, Expression):
+                if self.fixed:
+                    value = int(value * self.FIXED_BASE)
                 with ebpf.get_free_register(None) as src:
                     ebpf.r[src] = value
                     ebpf.append(Opcode.XADD + bits, self.base_register,
@@ -889,10 +987,16 @@ class MemoryDesc:
         elif isinstance(value, Expression):
             opcode = Opcode.STX
         else:
+            if self.fixed:
+                value = int(value * Expression.FIXED_BASE)
             ebpf.append(Opcode.ST + bits, self.base_register, 0,
                         addr, value)
             return
-        with value.calculate(None, isinstance(fmt, str) and fmt in 'qQ'
+        if self.fmt == "f" and not value.fixed:
+            value = value * Expression.FIXED_BASE
+        elif self.fmt != "f" and value.fixed:
+            value = value / Expression.FIXED_BASE
+        with value.calculate(None, isinstance(fmt, str) and fmt in 'qQf'
                             ) as (src, _):
             ebpf.append(opcode + bits, self.base_register, src, addr, 0)
 
@@ -903,6 +1007,7 @@ class LocalVar(MemoryDesc):
 
     def __init__(self, fmt='I'):
         self.fmt = fmt
+        self.fixed = fmt == "f"
 
     def __set_name__(self, owner, name):
         if isinstance(self.fmt, str):
@@ -936,7 +1041,9 @@ class MemoryMap:
                 offset = 0
             if isinstance(value, IAdd):
                 value = value.value
-                if isinstance(value, int):
+                if self.fmt == "f":
+                    value = int(value * self.FIXED_BASE)
+                if not isinstance(value, Expression):
                     with self.ebpf.get_free_register(None) as src:
                         self.ebpf.r[src] = value
                         self.ebpf.append(
@@ -947,6 +1054,8 @@ class MemoryMap:
             elif isinstance(value, Expression):
                 opcode = Opcode.STX
             else:
+                if self.fmt == "f":
+                    value = int(value * self.FIXED_BASE)
                 self.ebpf.append(Opcode.ST + Memory.fmt_to_opcode[self.fmt],
                                  dst, 0, offset, value)
                 return
@@ -975,6 +1084,7 @@ class PseudoFd(Expression):
     def __init__(self, ebpf, fd):
         self.ebpf = ebpf
         self.fd = fd
+        self.fixed = False
 
     @contextmanager
     def calculate(self, dst, long, force=False):
@@ -988,6 +1098,7 @@ class ktime(Expression):
     """a function that returns the current ktime in ns"""
     def __init__(self, ebpf):
         self.ebpf = ebpf
+        self.fixed = False
 
     @contextmanager
     def calculate(self, dst, long, force=False):
@@ -1030,27 +1141,34 @@ class RegisterDesc:
 
 
 class RegisterArray:
-    def __init__(self, ebpf, long, signed):
+    def __init__(self, ebpf, long, signed, fixed=False):
         self.ebpf = ebpf
         self.long = long
         self.signed = signed
+        self.fixed = fixed
 
     def __setitem__(self, no, value):
         self.ebpf.owners.add(no)
         if isinstance(value, Expression):
+            if self.fixed and not value.fixed:
+                value = value * Expression.FIXED_BASE
+            if not self.fixed and value.fixed:
+                value = value / Expression.FIXED_BASE
             with value.calculate(no, self.long, True):
                 pass
         else:
+            if self.fixed:
+                value = int(value * Expression.FIXED_BASE)
             self.ebpf._load_value(no, value)
 
     def __getitem__(self, no):
-        return Register(no, self.ebpf, self.long, self.signed)
+        return Register(no, self.ebpf, self.long, self.signed, self.fixed)
 
 
 
 class Temporary(Register):
-    def __init__(self, ebpf, long, signed):
-        super().__init__(None, ebpf, long, signed)
+    def __init__(self, ebpf, long, signed, fixed):
+        super().__init__(None, ebpf, long, signed, fixed)
         self.nos = []
         self.gfrs = []
 
@@ -1077,7 +1195,7 @@ class TemporaryDesc(RegisterDesc):
         ret = instance.__dict__.get(self.name, None)
         if ret is None:
             ret = instance.__dict__[self.name] = \
-                    Temporary(instance, arr.long, arr.signed)
+                    Temporary(instance, arr.long, arr.signed, arr.fixed)
         return ret
 
     def __set__(self, instance, value):
@@ -1118,11 +1236,13 @@ class EBPF:
         self.mh = MemoryMap(self, "h")
         self.mi = MemoryMap(self, "i")
         self.mq = MemoryMap(self, "q")
+        self.mf = MemoryMap(self, "f")
 
         self.r = RegisterArray(self, True, False)
         self.sr = RegisterArray(self, True, True)
         self.w = RegisterArray(self, False, False)
         self.sw = RegisterArray(self, False, True)
+        self.f = RegisterArray(self, True, True, True)
 
         self.owners = {1, 10}
 
@@ -1247,6 +1367,7 @@ class EBPF:
     stmp = TemporaryDesc(None, "sr")
     wtmp = TemporaryDesc(None, "w")
     swtmp = TemporaryDesc(None, "sw")
+    ftmp = TemporaryDesc(None, "f")
 
 
 for i in range(11):
@@ -1261,6 +1382,9 @@ for i in range(10):
 for i in range(10):
     setattr(EBPF, f"sw{i}", RegisterDesc(i, "sw"))
 
+for i in range(10):
+    setattr(EBPF, f"f{i}", RegisterDesc(i, "f"))
+
 
 class SubProgram:
     stack = 0
diff --git a/ebpfcat/ebpf_test.py b/ebpfcat/ebpf_test.py
index 19fadf2..0ffdac5 100644
--- a/ebpfcat/ebpf_test.py
+++ b/ebpfcat/ebpf_test.py
@@ -162,24 +162,142 @@ class Tests(TestCase):
              Instruction(opcode=O.LONG+O.RSH, dst=5, src=0, off=0, imm=2),
             ])
 
+    def test_fixed(self):
+        e = EBPF()
+        e.owners = {0, 1, 2, 3, 4, 5, 6}
+        e.f1 = e.r2 + 3
+        e.f3 = e.r4 + 3.5
+        e.f5 = e.f6 + 3
+        e.r1 = e.r2 + e.f3
+        e.f4 = e.f5 + e.f6
+        e.r1 = 2 - e.f2
+        e.r3 = 3.4 - e.r4
+        e.r5 = e.f6 % 4
+
+        e.f1 = e.r2 * 3
+        e.f3 = e.r4 * 3.5
+        e.f5 = e.f6 * 3
+        e.r1 = e.r2 * e.f3
+        e.f4 = e.f5 * e.f6
+
+        e.f1 = e.r2 / 3
+        e.f3 = e.r4 / 3.5
+        e.f5 = e.f6 / 3
+        e.r1 = e.r2 / e.f3
+        e.f4 = e.f5 / e.f6
+
+        e.f1 = e.r2 // 3
+        e.f3 = e.r4 // 3.5
+        e.f5 = e.f6 // 3
+        e.r1 = e.r2 // e.f3
+        e.f4 = e.f5 // e.f6
+
+        self.maxDiff = None
+        self.assertEqual(e.opcodes, [
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.ADD+O.LONG, dst=1, src=0, off=0, imm=3),
+           Instruction(opcode=O.MUL+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=3, src=4, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=3, src=0, off=0, imm=100000),
+           Instruction(opcode=O.ADD+O.LONG, dst=3, src=0, off=0, imm=350000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=5, src=6, off=0, imm=0),
+           Instruction(opcode=O.ADD+O.LONG, dst=5, src=0, off=0, imm=300000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MUL, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.ADD+O.LONG, dst=1, src=3, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=4, src=5, off=0, imm=0),
+           Instruction(opcode=O.REG+O.ADD+O.LONG, dst=4, src=6, off=0, imm=0),
+           Instruction(opcode=O.MOV+O.LONG, dst=1, src=0, off=0, imm=200000),
+           Instruction(opcode=O.REG+O.SUB+O.LONG, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.DIV, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.MOV+O.LONG, dst=3, src=0, off=0, imm=340000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=7, src=4, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MUL, dst=7, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.SUB+O.LONG, dst=3, src=7, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=3, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.LONG+O.MOV, dst=5, src=6, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MOD, dst=5, src=0, off=0, imm=400000),
+           Instruction(opcode=O.LONG+O.DIV, dst=5, src=0, off=0, imm=100000),
+
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MUL, dst=1, src=0, off=0, imm=3),
+           Instruction(opcode=O.LONG+O.MUL, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=3, src=4, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MUL, dst=3, src=0, off=0, imm=350000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=5, src=6, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.MUL, dst=5, src=0, off=0, imm=3),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.REG+O.LONG+O.MUL, dst=1, src=3, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.REG+O.MOV+O.LONG, dst=4, src=5, off=0, imm=0),
+           Instruction(opcode=O.REG+O.LONG+O.MUL, dst=4, src=6, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=4, src=0, off=0, imm=100000),
+
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.DIV+O.LONG, dst=1, src=0, off=0, imm=3),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=3, src=4, off=0, imm=0),
+           Instruction(opcode=O.DW, dst=7, src=0, off=0, imm=1410065408),
+           Instruction(opcode=O.W, dst=0, src=0, off=0, imm=2),
+           Instruction(opcode=O.MUL+O.REG+O.LONG, dst=3, src=7, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=3, src=0, off=0, imm=350000),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=5, src=6, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=5, src=0, off=0, imm=3),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.DW, dst=7, src=0, off=0, imm=1410065408),
+           Instruction(opcode=O.W, dst=0, src=0, off=0, imm=2),
+           Instruction(opcode=O.REG+O.LONG+O.MUL, dst=1, src=7, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG+O.REG, dst=1, src=3, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=4, src=5, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=4, src=0, off=0, imm=100000),
+           Instruction(opcode=O.DIV+O.LONG+O.REG, dst=4, src=6, off=0, imm=0),
+
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=1, src=0, off=0, imm=3),
+           Instruction(opcode=O.MUL+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=3, src=4, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=3, src=0, off=0, imm=100000),
+           Instruction(opcode=O.DIV+O.LONG, dst=3, src=0, off=0, imm=350000),
+           Instruction(opcode=O.MUL+O.LONG, dst=3, src=0, off=0, imm=100000),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=5, src=6, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG, dst=5, src=0, off=0, imm=300000),
+           Instruction(opcode=O.MUL+O.LONG, dst=5, src=0, off=0, imm=100000),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=1, src=2, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=1, src=0, off=0, imm=100000),
+           Instruction(opcode=O.DIV+O.LONG+O.REG, dst=1, src=3, off=0, imm=0),
+           Instruction(opcode=O.LONG+O.REG+O.MOV, dst=4, src=5, off=0, imm=0),
+           Instruction(opcode=O.DIV+O.LONG+O.REG, dst=4, src=6, off=0, imm=0),
+           Instruction(opcode=O.MUL+O.LONG, dst=4, src=0, off=0, imm=100000),
+        ])
+
     def test_local(self):
         class Local(EBPF):
             a = LocalVar('b')
             b = LocalVar('H')
             c = LocalVar('i')
             d = LocalVar('Q')
+            lf = LocalVar('f')
 
         e = Local(ProgType.XDP, "GPL")
         e.a = 5
         e.b = e.c >> 3
         e.d = e.r1
+        e.lf = 7
+        e.b = e.f1
 
         self.assertEqual(e.opcodes, [
             Instruction(opcode=O.B+O.ST, dst=10, src=0, off=-1, imm=5),
             Instruction(opcode=O.W+O.LD, dst=0, src=10, off=-8, imm=0),
             Instruction(opcode=O.ARSH, dst=0, src=0, off=0, imm=3),
             Instruction(opcode=O.REG+O.STX, dst=10, src=0, off=-4, imm=0),
-            Instruction(opcode=O.DW+O.STX, dst=10, src=1, off=-16, imm=0)])
+            Instruction(opcode=O.DW+O.STX, dst=10, src=1, off=-16, imm=0),
+            Instruction(opcode=O.DW+O.ST, dst=10, src=0, off=-20, imm=700000),
+            Instruction(opcode=O.LONG+O.REG+O.MOV, dst=0, src=1, off=0, imm=0),
+            Instruction(opcode=O.DIV, dst=0, src=0, off=0, imm=100000),
+            Instruction(opcode=O.REG+O.STX, dst=10, src=0, off=-4, imm=0),
+        ])
 
     def test_local_bits(self):
         class Local(EBPF):
@@ -375,7 +493,7 @@ class Tests(TestCase):
 
     def test_with(self):
         e = EBPF()
-        e.owners = set(range(11))
+        e.owners = set(range(9))
         with e.r2 > 3 as Else:
             e.r2 = 5
         with Else:
@@ -386,8 +504,16 @@ class Tests(TestCase):
             e.r5 = 7
         with Else:
             e.r7 = 8
-        self.assertEqual(e.opcodes,
-            [Instruction(opcode=0xb5, dst=2, src=0, off=2, imm=3),
+        with e.f4 > 3:
+            pass
+        with 3 > e.f4:
+            pass
+        with e.r4 > 3.5:
+            pass
+        with e.f4 > e.f2:
+            pass
+        self.assertEqual(e.opcodes, [
+             Instruction(opcode=0xb5, dst=2, src=0, off=2, imm=3),
              Instruction(opcode=0xb7, dst=2, src=0, off=0, imm=5),
              Instruction(opcode=0x5, dst=0, src=0, off=1, imm=0),
              Instruction(opcode=O.MOV+O.LONG, dst=6, src=0, off=0, imm=7),
@@ -396,7 +522,14 @@ class Tests(TestCase):
              Instruction(opcode=O.JLE, dst=4, src=0, off=2, imm=3),
              Instruction(opcode=O.MOV+O.LONG, dst=5, src=0, off=0, imm=7),
              Instruction(opcode=O.JMP, dst=0, src=0, off=1, imm=0),
-             Instruction(opcode=O.MOV+O.LONG, dst=7, src=0, off=0, imm=8)])
+             Instruction(opcode=O.MOV+O.LONG, dst=7, src=0, off=0, imm=8),
+             Instruction(opcode=O.JSLE, dst=4, src=0, off=0, imm=300000),
+             Instruction(opcode=O.JSGE, dst=4, src=0, off=0, imm=300000),
+             Instruction(opcode=O.REG+O.MOV+O.LONG, dst=9, src=4, off=0, imm=0),
+             Instruction(opcode=O.MUL+O.LONG, dst=9, src=0, off=0, imm=100000),
+             Instruction(opcode=O.JLE, dst=9, src=0, off=0, imm=350000),
+             Instruction(opcode=O.REG+O.JSLE, dst=4, src=2, off=0, imm=0),
+        ])
 
     def test_with_inversion(self):
         e = EBPF()
@@ -602,10 +735,15 @@ class Tests(TestCase):
     def test_absolute(self):
         e = EBPF()
         e.r7 = abs(e.r1)
+        e.f3 = abs(e.f1)
         self.assertEqual(e.opcodes, [
             Instruction(opcode=O.LONG+O.REG+O.MOV, dst=7, src=1, off=0, imm=0),
-            Instruction(opcode=O.JGE, dst=7, src=0, off=1, imm=0),
-            Instruction(opcode=O.LONG+O.NEG, dst=7, src=0, off=0, imm=0)])
+            Instruction(opcode=O.JSGE, dst=7, src=0, off=1, imm=0),
+            Instruction(opcode=O.LONG+O.NEG, dst=7, src=0, off=0, imm=0),
+            Instruction(opcode=O.REG+O.MOV+O.LONG, dst=3, src=1, off=0, imm=0),
+            Instruction(opcode=O.JSGE, dst=3, src=0, off=1, imm=0),
+            Instruction(opcode=O.NEG+O.LONG, dst=3, src=0, off=0, imm=0),
+        ])
 
     def test_jump_data(self):
         e = EBPF()
@@ -712,6 +850,10 @@ class Tests(TestCase):
                 e.r7 = e.tmp
             e.tmp = 2
             e.r3 = e.tmp
+        with e.ftmp:
+            e.ftmp = 3
+            e.r3 = e.ftmp
+            e.ftmp = e.r3 * 3.5
         self.assertEqual(e.opcodes, [
             Instruction(opcode=O.MOV+O.LONG, dst=0, src=0, off=0, imm=7),
             Instruction(opcode=O.MOV+O.LONG, dst=2, src=0, off=0, imm=3),
@@ -719,7 +861,12 @@ class Tests(TestCase):
             Instruction(opcode=O.MOV+O.LONG, dst=4, src=0, off=0, imm=5),
             Instruction(opcode=O.MOV+O.LONG+O.REG, dst=7, src=4, off=0, imm=0),
             Instruction(opcode=O.MOV+O.LONG, dst=2, src=0, off=0, imm=2),
-            Instruction(opcode=O.MOV+O.LONG+O.REG, dst=3, src=2, off=0, imm=0)
+            Instruction(opcode=O.MOV+O.LONG+O.REG, dst=3, src=2, off=0, imm=0),
+            Instruction(opcode=O.MOV+O.LONG, dst=2, src=0, off=0, imm=300000),
+            Instruction(opcode=O.MOV+O.REG+O.LONG, dst=3, src=2, off=0, imm=0),
+            Instruction(opcode=O.DIV+O.LONG, dst=3, src=0, off=0, imm=100000),
+            Instruction(opcode=O.MOV+O.REG+O.LONG, dst=2, src=3, off=0, imm=0),
+            Instruction(opcode=O.LONG+O.MUL, dst=2, src=0, off=0, imm=350000),
             ])
 
     def test_ktime(self):
@@ -775,15 +922,18 @@ class KernelTests(TestCase):
         class Global(EBPF):
             map = ArrayMap()
             ar = map.globalVar()
-            aw = map.globalVar()
+            aw = map.globalVar("h")
 
         class Sub(SubProgram):
             br = Global.map.globalVar()
-            bw = Global.map.globalVar()
+            bw = Global.map.globalVar("h")
+            bf = Global.map.globalVar("f")
 
             def program(self):
+                self.bw = 4
                 self.br -= -33
                 self.bw = self.br + 3
+                self.bf = self.br / 3.5 + self.bf
 
         s1 = Sub()
         s2 = Sub()
@@ -801,9 +951,11 @@ class KernelTests(TestCase):
         self.assertEqual(e.aw, 11)
         self.assertEqual(s1.br, 33)
         self.assertEqual(s1.bw, 36)
+        self.assertEqual(s2.bf, 9.42857)
         s1.br = 3
         s2.br *= 5
         e.ar = 1111
+        s2.bf = 1.3
         self.assertEqual(e.ar, 1111)
         self.assertEqual(e.aw, 11)
         self.assertEqual(s1.br, 3)
@@ -817,6 +969,7 @@ class KernelTests(TestCase):
         self.assertEqual(s1.bw, 39)
         self.assertEqual(s2.br, 198)
         self.assertEqual(s2.bw, 201)
+        self.assertEqual(s2.bf, 57.87142)
 
     def test_minimal(self):
         class Local(EBPF):
diff --git a/ebpfcat/hashmap.py b/ebpfcat/hashmap.py
index aacea52..8e5e61c 100644
--- a/ebpfcat/hashmap.py
+++ b/ebpfcat/hashmap.py
@@ -28,6 +28,7 @@ class HashGlobalVar(Expression):
         self.count = count
         self.fmt = fmt
         self.signed = fmt.islower()
+        self.fixed = fmt == "f"
 
     @contextmanager
     def get_address(self, dst, long, force=False):
-- 
GitLab