Closed mdlayher closed 4 years ago
- finding another way to hide/unexport the
Socket
type andNewConn
constructors
You've probably already thought of this, but any possibility of using an internal package that both netlink
and nltest
could import? I'd feel more comfortable with this change than using //go:linkname
.
- unexporting or removing
Message.Marshal/UnmarshalBinary
I can confirm I'm not using these functions with our internal clients.
- consider removing
HeaderType
andHeaderFlags
prefixes from typed constants
In my own code, I generally like to do this when I feel like I can get away with it.
netlink.Acknowledge
is certainly a lot nicer than netlink.HeaderFlagsAcknowledge
. I don't think it would be very confusing for callers, because they're probably not using the netlink package without some level of domain knowledge about netlink itself, in which case it wouldn't be hard to figure out what the names Acknowledge
or Request
refer to.
The one slightly iffy case seems to be netlink.HeaderTypeError
, which becomes netlink.Error
under the proposed change. Perhaps this identifier is valuable in another context. I discuss this more in the final paragraph.
I will also note that ConnOption
constants don't use the prefixes, while HeaderFlags
and HeaderType
constants do. Perhaps a consistent choice should be made throughout.
No strong opinions on this matter either way.
- finding another way to hide/unexport the
Socket
type andNewConn
constructors
This is a tough one. I've thought about it quite a bit, but I don't think it's really possible. Linker trickery doesn't feel like it belongs in such a place, and other alternatives don't seem to work. For example:
You've probably already thought of this, but any possibility of using an internal package that both
netlink
andnltest
could import? I'd feel more comfortable with this change than using//go:linkname
.
Assume this package is github.com/mdlayher/netlink/internal/nltransport
, and it declares nltransport.Socket
, which is the current netlink.Socket
. The problem is that netlink.Socket
references netlink.Message
, so the hypothetical nltransport
must either:
netlink
, which means netlink
cannot import nltransport
, so this is a no-goMessage
, which is awkward and strangeThe nice thing about the existing Socket
interface is that it's high level, pretty small, and easy for nltest
to implement. To get rid of it, it feels like you have to either do very ugly things like linker tricks, or make netlink
swallow nltest
completely. I don't think either option is worth the cost.
One final point: Look at crypto/tls
and net/smtp
. Both have a higher level func Dial
and a lower level func Client
, func Server
, func NewClient
etc. In the lower level functions, the argument is net.Conn
, which represents a network transport. By analogy, a netlink.Socket
is a transport for a netlink.Conn
: it can send and receive messages, which Conn
builds on top of. I think that's totally fine, even if the only two implementations ever used are netlink.conn
and nltest.socket
.
In summary, I think Socket
and NewConn
should stay, because the alternatives are not nice at all.
- consider dropping
Conn.ReadWriteCloser
I think that's a good idea. The SyscallConn
API is more flexible, and does everything the ReadWriteCloser
API can do, and more.
In addition, if there are other things you think are worth revisiting for 1.0.0, please do let me know.
Throughout the standard library, errors from system calls are decorated with additional details, e.g. os.SyscallError
, net.OpError
. Package netlink doesn't do any of this at the moment, as far as I can tell. It seems to return syscall.Errno
values up to the callers.
Perhaps this is something worth thinking about. For example, when does sendmsg(2)
fail on a netlink socket? If and when it does, is the syscall.Errno
value enough for the caller to be able to diagnose the cause of the error?
This circles back to the netlink.Error
naming issue I mentioned above. Is there any point in having a type that captures operational errors and decorates them with additional context? If there is, what would the type be named? Would it be netlink.Error
? Or perhaps netlink.OpError
?
Personally, all the errors I've gotten while using netlink have been in the form of netlink error messages, rather than system call errors from operations such as calling conn.Send
, so I haven't had this problem. That being said, I have not used netlink in very advanced ways, so perhaps I have missed something. But having noticed this detail about error handling while I was working on the code, I thought I would at least bring it up for discussion.
Good initiative, doesn't look like any major changes, maybe apart from removing Conn.RWC
. I don't use it in any of my packages, though wouldn't this play nice with the new 1.12 runtime poller? Admittedly I haven't followed up on the changes here since before Conn.SyscallConn
was introduced.
- consider removing
HeaderType
andHeaderFlags
prefixes from typed constants
Definitely, IDEs (at least VSCode) will show autocomplete hints in the form of HeaderFlagsRequest HeaderFlags
, which stutters. Request HeaderFlags
would be easier to read there. Could this conflict with other identifiers in the future? I'd argue this is unlikely, since the term 'request' is already used for this header flag in Netlink lingo (we're just copying kernel terminology), and the protocol is inherently message-oriented without any request/response abstraction.
Agreed with @terinjokes' and @acln0's feedback, great input :+1:
Thanks for the comments so far! I'm going to be traveling for a week or so, but I will revisit this in a week's time and review everything again.
- unexporting or removing
Message.Marshal/UnmarshalBinary
- changing signature of
Config.Groups
andConn.Join/LeaveGroup
to passgroup int
rather thangroup uint32
Some of my libraries are using these functions and its on my todo list, to replace them. But these libraries also use go modules. So improving here your API is just fine :+1:
- consider removing
HeaderType
andHeaderFlags
prefixes from typed constants
This change will improve readability of code more than it could confuse - I think. So, I really like it :+1:
Some of my libraries are using these functions and its on my todo list, to replace them. But these libraries also use go modules. So improving here your API is just fine
@florianl if you have a valid use case for the Message marshaling functions, I'm happy to not remove them; I just assumed it was unnecessary to have them exported. Can you link me to your code?
One example in my code is https://github.com/florianl/go-nfqueue/blob/master/nfqueue.go#L83 - here I set the verdict for nfqueue. But this can be rewritten with AttributeDecoder
.
Ah! So I don't think MarshalAttributes is going anywhere because it can be convenient sometimes; I was talking about the marshaling methods on the Message type.
Sorry for mixing this up with the Attributes. For keeping Message.Marshal/UnmarshalBinary
exported, I don't see a use case.
The 1.13 error handling stuff might shake things up a bit, so let's hold off until at least that point.
Once 1.12 hits and we're able to land runtime network poller support with proper timeouts, I think it's finally time to embrace semver and Go modules, and tag v1.0.0.
Before we do that, I'm debating if it's worth revising some previous interface decisions, such as:
Config.Groups
andConn.Join/LeaveGroup
to passgroup int
rather thangroup uint32
This is passed as a 32-bit C
int
to the kernel, so Go's 64-bitint
on 64-bit systems would need a sanity check.int
is generally a good default and the most common integer type in Go by far;uint32
requires an explicit cast unless you're using an untyped constant (which is generally the case).Message.Marshal/UnmarshalBinary
If you're importing this package, you're almost certainly using
Conn
which takes care of these internally. We could also do faster and more clever things if we didn't have to worry about allocating for individual messages, or making a copy of byte slices during unmarshaling.DONE: consider removing
HeaderType
andHeaderFlags
prefixes from typed constantsI'm admittedly a little iffy on this, but in recent projects I tend to prefer more succinct names like
netlink.Request
, even if the type remainsRequest HeaderFlags = 1
. Is there a possibility of conflict in header types/flags with other current or future exported identifiers? Would this be too confusing in calling code?Socket
type andNewConn
constructorsThese are really only meant for nltest; I had considered doing unsafe
//go:linkname
at one point but can't remember why I didn't go with it. It's not great, but it might be better than adding generally useless stuff to the exported 1.0.0 API.DONE: - consider dropping
Conn.ReadWriteCloser
This is only used in github.com/mdlayher/kobject as far as I know, and I think that its use there could be superseded by
Conn.SyscallConn
and methods there.I think that about covers my thoughts. I know it's a lot, and I don't expect others to have strong opinions on all or any of these, but I would appreciate your feedback. In addition, if there are other things you think are worth revisiting for 1.0.0, please do let me know.
I'm going to tag some of the regular contributors and users of this package in hopes that folks can help me work things out, although I encourage feedback from all who come across this issue.
/cc @florianl @ti-mo @terinjokes @jsimonetti @stapelberg @acln0