| ... | ... | @@ -3,9 +3,13 @@ |
| 3 | 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | | usingnamespace std.os; |
| 6 | usingnamespace std.os.linux; |
| 7 | 7 | const std = @import("../../std.zig"); |
| 8 | const errno = getErrno; |
| 9 | const unexpectedErrno = std.os.unexpectedErrno; |
| 8 | 10 | const expectEqual = std.testing.expectEqual; |
| 11 | const expectError = std.testing.expectError; |
| 12 | const expect = std.testing.expect; |
| 9 | 13 | |
| 10 | 14 | // instruction classes |
| 11 | 15 | pub const LD = 0x00; |
| ... | ... | @@ -1323,7 +1327,7 @@ pub const ProgAttachAttr = extern struct { |
| 1323 | 1327 | }; |
| 1324 | 1328 | |
| 1325 | 1329 | /// struct used by Cmd.prog_test_run command |
| 1326 | | pub const TestAttr = extern struct { |
| 1330 | pub const TestRunAttr = extern struct { |
| 1327 | 1331 | prog_fd: fd_t, |
| 1328 | 1332 | retval: u32, |
| 1329 | 1333 | |
| ... | ... | @@ -1484,3 +1488,176 @@ pub const Attr = extern union { |
| 1484 | 1488 | enable_stats: EnableStatsAttr, |
| 1485 | 1489 | iter_create: IterCreateAttr, |
| 1486 | 1490 | }; |
| 1491 | |
| 1492 | pub const Log = struct { |
| 1493 | level: u32, |
| 1494 | buf: []u8, |
| 1495 | }; |
| 1496 | |
| 1497 | pub fn map_create(map_type: MapType, key_size: u32, value_size: u32, max_entries: u32) !fd_t { |
| 1498 | var attr = Attr{ |
| 1499 | .map_create = std.mem.zeroes(MapCreateAttr), |
| 1500 | }; |
| 1501 | |
| 1502 | attr.map_create.map_type = @enumToInt(map_type); |
| 1503 | attr.map_create.key_size = key_size; |
| 1504 | attr.map_create.value_size = value_size; |
| 1505 | attr.map_create.max_entries = max_entries; |
| 1506 | |
| 1507 | const rc = bpf(.map_create, &attr, @sizeOf(MapCreateAttr)); |
| 1508 | return switch (errno(rc)) { |
| 1509 | 0 => @intCast(fd_t, rc), |
| 1510 | EINVAL => error.MapTypeOrAttrInvalid, |
| 1511 | ENOMEM => error.SystemResources, |
| 1512 | EPERM => error.AccessDenied, |
| 1513 | else => |err| unexpectedErrno(rc), |
| 1514 | }; |
| 1515 | } |
| 1516 | |
| 1517 | test "map_create" { |
| 1518 | const map = try map_create(.hash, 4, 4, 32); |
| 1519 | defer std.os.close(map); |
| 1520 | } |
| 1521 | |
| 1522 | pub fn map_lookup_elem(fd: fd_t, key: []const u8, value: []u8) !void { |
| 1523 | var attr = Attr{ |
| 1524 | .map_elem = std.mem.zeroes(MapElemAttr), |
| 1525 | }; |
| 1526 | |
| 1527 | attr.map_elem.map_fd = fd; |
| 1528 | attr.map_elem.key = @ptrToInt(key.ptr); |
| 1529 | attr.map_elem.result.value = @ptrToInt(value.ptr); |
| 1530 | |
| 1531 | const rc = bpf(.map_lookup_elem, &attr, @sizeOf(MapElemAttr)); |
| 1532 | switch (errno(rc)) { |
| 1533 | 0 => return, |
| 1534 | EBADF => return error.BadFd, |
| 1535 | EFAULT => unreachable, |
| 1536 | EINVAL => return error.FieldInAttrNeedsZeroing, |
| 1537 | ENOENT => return error.NotFound, |
| 1538 | EPERM => return error.AccessDenied, |
| 1539 | else => |err| return unexpectedErrno(rc), |
| 1540 | } |
| 1541 | } |
| 1542 | |
| 1543 | pub fn map_update_elem(fd: fd_t, key: []const u8, value: []const u8, flags: u64) !void { |
| 1544 | var attr = Attr{ |
| 1545 | .map_elem = std.mem.zeroes(MapElemAttr), |
| 1546 | }; |
| 1547 | |
| 1548 | attr.map_elem.map_fd = fd; |
| 1549 | attr.map_elem.key = @ptrToInt(key.ptr); |
| 1550 | attr.map_elem.result = .{ .value = @ptrToInt(value.ptr) }; |
| 1551 | attr.map_elem.flags = flags; |
| 1552 | |
| 1553 | const rc = bpf(.map_update_elem, &attr, @sizeOf(MapElemAttr)); |
| 1554 | switch (errno(rc)) { |
| 1555 | 0 => return, |
| 1556 | E2BIG => return error.ReachedMaxEntries, |
| 1557 | EBADF => return error.BadFd, |
| 1558 | EFAULT => unreachable, |
| 1559 | EINVAL => return error.FieldInAttrNeedsZeroing, |
| 1560 | ENOMEM => return error.SystemResources, |
| 1561 | EPERM => return error.AccessDenied, |
| 1562 | else => |err| return unexpectedErrno(err), |
| 1563 | } |
| 1564 | } |
| 1565 | |
| 1566 | pub fn map_delete_elem(fd: fd_t, key: []const u8) !void { |
| 1567 | var attr = Attr{ |
| 1568 | .map_elem = std.mem.zeroes(MapElemAttr), |
| 1569 | }; |
| 1570 | |
| 1571 | attr.map_elem.map_fd = fd; |
| 1572 | attr.map_elem.key = @ptrToInt(key.ptr); |
| 1573 | |
| 1574 | const rc = bpf(.map_delete_elem, &attr, @sizeOf(MapElemAttr)); |
| 1575 | switch (errno(rc)) { |
| 1576 | 0 => return, |
| 1577 | EBADF => return error.BadFd, |
| 1578 | EFAULT => unreachable, |
| 1579 | EINVAL => return error.FieldInAttrNeedsZeroing, |
| 1580 | ENOENT => return error.NotFound, |
| 1581 | EPERM => return error.AccessDenied, |
| 1582 | else => |err| return unexpectedErrno(err), |
| 1583 | } |
| 1584 | } |
| 1585 | |
| 1586 | test "map lookup, update, and delete" { |
| 1587 | const key_size = 4; |
| 1588 | const value_size = 4; |
| 1589 | const map = try map_create(.hash, key_size, value_size, 1); |
| 1590 | defer std.os.close(map); |
| 1591 | |
| 1592 | const key = std.mem.zeroes([key_size]u8); |
| 1593 | var value = std.mem.zeroes([value_size]u8); |
| 1594 | |
| 1595 | // fails looking up value that doesn't exist |
| 1596 | expectError(error.NotFound, map_lookup_elem(map, &key, &value)); |
| 1597 | |
| 1598 | // succeed at updating and looking up element |
| 1599 | try map_update_elem(map, &key, &value, 0); |
| 1600 | try map_lookup_elem(map, &key, &value); |
| 1601 | |
| 1602 | // fails inserting more than max entries |
| 1603 | const second_key = [key_size]u8{ 0, 0, 0, 1 }; |
| 1604 | expectError(error.ReachedMaxEntries, map_update_elem(map, &second_key, &value, 0)); |
| 1605 | |
| 1606 | // succeed at deleting an existing elem |
| 1607 | try map_delete_elem(map, &key); |
| 1608 | expectError(error.NotFound, map_lookup_elem(map, &key, &value)); |
| 1609 | |
| 1610 | // fail at deleting a non-existing elem |
| 1611 | expectError(error.NotFound, map_delete_elem(map, &key)); |
| 1612 | } |
| 1613 | |
| 1614 | pub fn prog_load( |
| 1615 | prog_type: ProgType, |
| 1616 | insns: []const Insn, |
| 1617 | log: ?*Log, |
| 1618 | license: []const u8, |
| 1619 | kern_version: u32, |
| 1620 | ) !fd_t { |
| 1621 | var attr = Attr{ |
| 1622 | .prog_load = std.mem.zeroes(ProgLoadAttr), |
| 1623 | }; |
| 1624 | |
| 1625 | attr.prog_load.prog_type = @enumToInt(prog_type); |
| 1626 | attr.prog_load.insns = @ptrToInt(insns.ptr); |
| 1627 | attr.prog_load.insn_cnt = @intCast(u32, insns.len); |
| 1628 | attr.prog_load.license = @ptrToInt(license.ptr); |
| 1629 | attr.prog_load.kern_version = kern_version; |
| 1630 | |
| 1631 | if (log) |l| { |
| 1632 | attr.prog_load.log_buf = @ptrToInt(l.buf.ptr); |
| 1633 | attr.prog_load.log_size = @intCast(u32, l.buf.len); |
| 1634 | attr.prog_load.log_level = l.level; |
| 1635 | } |
| 1636 | |
| 1637 | const rc = bpf(.prog_load, &attr, @sizeOf(ProgLoadAttr)); |
| 1638 | return switch (errno(rc)) { |
| 1639 | 0 => @intCast(fd_t, rc), |
| 1640 | EACCES => error.UnsafeProgram, |
| 1641 | EFAULT => unreachable, |
| 1642 | EINVAL => error.InvalidProgram, |
| 1643 | EPERM => error.AccessDenied, |
| 1644 | else => |err| unexpectedErrno(err), |
| 1645 | }; |
| 1646 | } |
| 1647 | |
| 1648 | test "prog_load" { |
| 1649 | // this should fail because it does not set r0 before exiting |
| 1650 | const bad_prog = [_]Insn{ |
| 1651 | Insn.exit(), |
| 1652 | }; |
| 1653 | |
| 1654 | const good_prog = [_]Insn{ |
| 1655 | Insn.mov(.r0, 0), |
| 1656 | Insn.exit(), |
| 1657 | }; |
| 1658 | |
| 1659 | const prog = try prog_load(.socket_filter, &good_prog, null, "MIT", 0); |
| 1660 | defer std.os.close(prog); |
| 1661 | |
| 1662 | expectError(error.UnsafeProgram, prog_load(.socket_filter, &bad_prog, null, "MIT", 0)); |
| 1663 | } |