Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mark filter fix #54

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 73 additions & 6 deletions bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ var (
const (
bpfMAXINSTR = 4096

failedJump = uint8(255)

bpfVerdictAccept = 0xffffffff
bpfVerdictReject = 0x00000000
)
Expand Down Expand Up @@ -185,7 +187,6 @@ func compareValues(filters []ConnAttr) []bpf.RawInstruction {
func filterAttribute(filters []ConnAttr) []bpf.RawInstruction {
var raw []bpf.RawInstruction
nested := len(filterCheck[filters[0].Type].nest)
failed := uint8(255)

// sizeof(nlmsghdr) + sizeof(nfgenmsg) = 20
tmp := bpf.RawInstruction{Op: unix.BPF_LD | unix.BPF_IMM, K: 0x14}
Expand All @@ -200,7 +201,7 @@ func filterAttribute(filters []ConnAttr) []bpf.RawInstruction {
raw = append(raw, tmp)

// jump, if nest not found
tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: 0, Jt: failed}
tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: 0, Jt: failedJump}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_ALU | unix.BPF_ADD | unix.BPF_K, K: 4}
Expand All @@ -214,7 +215,7 @@ func filterAttribute(filters []ConnAttr) []bpf.RawInstruction {
tmp = bpf.RawInstruction{Op: unix.BPF_LD | unix.BPF_B | unix.BPF_ABS, K: 0xfffff00c}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: 0, Jt: failed}
tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: 0, Jt: failedJump}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_MISC | unix.BPF_TAX}
Expand All @@ -234,9 +235,9 @@ func filterAttribute(filters []ConnAttr) []bpf.RawInstruction {
// Failed jumps are set to 255. Now we correct them to the actual failed jump instruction
j := uint8(1)
for i := len(raw) - 1; i > 0; i-- {
if (raw[i].Jt == 255) && (raw[i].Op == unix.BPF_JMP|unix.BPF_JEQ|unix.BPF_K) {
if (raw[i].Jt == failedJump) && (raw[i].Op == unix.BPF_JMP|unix.BPF_JEQ|unix.BPF_K) {
raw[i].Jt = j - jump
} else if (raw[i].Jf == 255) && (raw[i].Op == unix.BPF_JMP|unix.BPF_JEQ|unix.BPF_K) {
} else if (raw[i].Jf == failedJump) && (raw[i].Op == unix.BPF_JMP|unix.BPF_JEQ|unix.BPF_K) {
raw[i].Jf = j - 1
}
j++
Expand Down Expand Up @@ -302,7 +303,12 @@ func constructFilter(subsys Table, filters []ConnAttr) ([]bpf.RawInstruction, er
// We can not simple range over the map, because the order of selected items can vary
for key := 0; key <= int(attrMax); key++ {
if x, ok := filterMap[ConnAttrType(key)]; ok {
tmp = filterAttribute(x)
switch key {
case int(AttrMark):
tmp = filterMarkAttribute(x)
default:
tmp = filterAttribute(x)
}
raw = append(raw, tmp...)
}
}
Expand All @@ -317,6 +323,67 @@ func constructFilter(subsys Table, filters []ConnAttr) ([]bpf.RawInstruction, er
return raw, nil
}

func filterMarkAttribute(filters []ConnAttr) []bpf.RawInstruction {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! 🙏

As filterMarkAttribute() is similar to filterAttribute() I'm wondering if both functions should be combined into one. What do you think?

I'm testing it and it seems to be working fine. Please let me know what do you think about it. If you say it's ok, I will add tests which will cover this filter.

Adding a test would be great 👍

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dvomartin are you still working on this?

Copy link
Contributor Author

@dvomartin dvomartin Nov 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @florianl,
sorry, I was sick and forgot about this :)

I can try to prepare second merge request which will adjust original function(s), but I'm afraid there will be too many ifs.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dvomartin for #56 and hope you're fine again.

would you mind sharing your testcase? I'm fine with refactoring code later on with this (#54) PR applied, if we can make sure with a testcase, that things don't break.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure,
I have added some tests. My tests are slightly different - instructions are visible and invalid instructions in failed tests are printed to quickly find where is the problem.
I also added possibility to enable debug option to Nfct object which will print bpf instructions if enabled.

Print format is the same as in original C library to quickly compare results.

PR is still marked as WIP. It should be OK as it is, but I want to verify some things before I create final PR.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the update!
please feel free to mark the PR as ready for review, if you are.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be ready. Please check the changes.
I can update your original tests to the same format in separate PR, if you want.

var raw []bpf.RawInstruction

// sizeof(nlmsghdr) + sizeof(nfgenmsg) = 20
tmp := bpf.RawInstruction{Op: unix.BPF_LD | unix.BPF_IMM, K: 0x14}
raw = append(raw, tmp)

// find final attribute
tmp = bpf.RawInstruction{Op: unix.BPF_LDX | unix.BPF_IMM, K: uint32(filterCheck[filters[0].Type].ct)}
raw = append(raw, tmp)
tmp = bpf.RawInstruction{Op: unix.BPF_LD | unix.BPF_B | unix.BPF_ABS, K: 0xfffff00c}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: 0, Jt: 2}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_MISC | unix.BPF_TAX}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_LD | unix.BPF_W | unix.BPF_IND, K: uint32(len(filters[0].Data))}
raw = append(raw, tmp)

tmp = bpf.RawInstruction{Op: unix.BPF_MISC | unix.BPF_TAX}
raw = append(raw, tmp)

for _, filter := range filters {
var dataLen = len(filter.Data)
for i := 0; i < (int(dataLen) / 4); i++ {
mask := encodeValue(filter.Mask[i*4 : (i+1)*4])
tmp = bpf.RawInstruction{Op: unix.BPF_ALU | unix.BPF_AND | unix.BPF_K, K: mask}
raw = append(raw, tmp)
val := encodeValue(filter.Data[i*4 : (i+1)*4])
val &= mask
tmp = bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, K: val, Jt: failedJump}
raw = append(raw, tmp)
tmp = bpf.RawInstruction{Op: unix.BPF_MISC | unix.BPF_TXA}
raw = append(raw, tmp)
}
}

var j uint8 = 1

// Failed jumps are set to 255. Now we correct them to the actual failed jump instruction
for i := len(raw) - 1; i > 0; i-- {
if (raw[i].Jt == failedJump) && (raw[i].Op == unix.BPF_JMP|unix.BPF_JEQ|unix.BPF_K) {
raw[i].Jt = j
}
j++
}

// negate filter
if filters[0].Negate {
raw = append(raw, bpf.RawInstruction{Op: unix.BPF_JMP | unix.BPF_JA, K: 1})
}

// reject
raw = append(raw, bpf.RawInstruction{Op: unix.BPF_RET | unix.BPF_K, K: bpfVerdictReject})

return raw
}

func (nfct *Nfct) attachFilter(subsys Table, filters []ConnAttr) error {

bpfFilters, err := constructFilter(subsys, filters)
Expand Down