Reply by Don Y December 6, 20132013-12-06
Hi George,

On 12/4/2013 12:53 AM, George Neuner wrote:

>> So, a learning opportunity is lost. It would have been informative >> for them to crank out another paper explaining what the problems >> with the 256 bit implementation were that caused it not to be >> pursued "formally". > > There was quite a bit they did agree on. AFAIK the main arguments > were over how much to increase signing strength and whether increased > flexibility justified adding a second signature. > > I worked with 2 of the proposed extensions:
> The first was Tanenbaum's own proposal, which actually defined only > 224 bits but reserved bits for future strengthening of the signature. > > The second was Queensland's proposal. It defined 288 bits (36 bytes) > which was an unwieldy length but featured independent signing of the > user port field which made delegation simpler: a surrogate could take > an existing ticket and fill in a new user without needing the object > server to resign the rights. > > There also was talk of making Amoeba ids 64 bits, which Tanenbaum's > structure could accommodate. Queensland's structure would have grown > to 320 bits, but in either case all the fields would have been 32-bit > aligned (so hopefully quicker to work with). > > And it was expected that both memory sizes and network speeds would be > significantly increased in the near future, so nobody really was > worried about ticket sizes.
So, if they had one (or two) implementations, why not release either/both of them? And/or a paper(s) describing the pros/cons of each? Academics seem to *live* to write papers!! :>
> When Tanenbaum announced 256-bit capabilities for v4, I assumed that > dual signatures had lost because everyone previously had agreed that > the existing 48-bit signing was insufficient. It didn't seem likely > that Queensland's dual signatures would be squeezed into 256 bits. > > ??? It's all academic at this point.
Yes. Perhaps I will try writing to AST to see if there are any odds and ends hiding in a private archive. From past experiences, that has been a workable means of getting at things that weren't formally "released" or that may have had too many blemishes and too little time to clean up.
>> I'd also like to have seen how they handled passing the capability >> to a surrogate (e.g., how do you interpose a "debugger" agent >> without the actor being debugged having to "cooperate", etc.) > > Simple: you couldn't. Not that it would have been impossible, but > Amoeba didn't provide any way to do it. > > FLIP made connections based on *private* identifiers and maintained > specific client:server pair associations for stateful services. It > was possible for a debugger to impersonate the *public* port id of > either endpoint (or even both), but it was not possible to break into > an existing connection, nor could the debugger steer new connections > to itself if the endpoint was already running ... FLIP would simply > see the debugger as yet another instance of the requested target.
In Mach, I can slip an agent into any "communication path" (which, after all, is what all these ports represent) by manipulating the task (which, of course, is just another "object" -- represented by the same sorts of mechanisms!) directly (using an actor of very high privilege -- i.e., one holding send rights to the port that represents the task being modified!)
> WRT surrogates, I'm not sure what really is the question. You either > connect to the surrogate directly and pass the ticket in a message, or > name the ticket and stick it in the directory where the surrogate can > find it.
Imagine a file (ick). "Owner" owns the file. He creates a restricted capability (read+write access, but no delete) for that file and passes it to "Task". "Task" is charged with scanning the contents of the file (perhaps it is a structured array of data samples for a waveform) and altering them based on some particular criteria. "Task" wants to invoke a service that is very good at analyzing waveforms -- "Analyzer". But, there is no reason "Analyzer" needs to modify the file -- analysis doesn't require the ability to alter the file's contents! So, "Task" wants to hand the file (as represented by it's "capability") off to "Analyzer". However, when Analyzer tries to access the contents of the file (which it does by talking to the server that *backs* that file object), the server notices that "Analyzer" is not the entity for which the capability was created/signed. Furthermore, "Task" can't create a (*further*) restricted capability for the file because the capability that "Task" holds is not the OWNER capability (at least one of the rights bits has been cleared... giving him *limited* -- read+write; no delete -- access to that object). "Analyzer", in turn, may want to pass the file on to some other actor to perform some particular analysis aspect on behalf of Analyzer (who is doing this on behalf of Task). So, you want to be able to take *any* object "Handle" (returning to my terminology) and adjust the "authorizations" downward... regardless of whether you are the "paramount" holder of that object. And, keep passing less_than_or_equal rights along the chain.
>> And, the costs as tasks migrate (IIRC, Amoeba really didn't >> migrate tasks as much as "picking a suitable INITIAL HOST" >> for a particular task) > > Yes. Amoeba didn't do task migration out of the box - it simply > provided location transparency so that migration services could be > added (relatively) easily on top of it.
Well, it's still a fair bit of work bottling up an executing task, all its state and shipping it off to another CPU in a potentially heterogeneous execution environment! :> One of Mach's big IPC/RPC performance hits came from having to convey type information in a neutral fashion across the network. (Not just big/little Endian-ness but, also, representations of floats, etc.) That's where my use of a VM (at the application level) is a win.
Reply by George Neuner December 4, 20132013-12-04
Hi Don,

On Mon, 02 Dec 2013 19:34:53 -0700, Don Y <this@isnotme.com> wrote:

>So, a learning opportunity is lost. It would have been informative >for them to crank out another paper explaining what the problems >with the 256 bit implementation were that caused it not to be >pursued "formally".
There was quite a bit they did agree on. AFAIK the main arguments were over how much to increase signing strength and whether increased flexibility justified adding a second signature. I worked with 2 of the proposed extensions: 48: server port 32: object 32: rights 48: user port 64: signature 32: reserved and 48: server port 32: object 32: rights 64: signature_1 48: user port 64: signature_2 The first was Tanenbaum's own proposal, which actually defined only 224 bits but reserved bits for future strengthening of the signature. The second was Queensland's proposal. It defined 288 bits (36 bytes) which was an unwieldy length but featured independent signing of the user port field which made delegation simpler: a surrogate could take an existing ticket and fill in a new user without needing the object server to resign the rights. There also was talk of making Amoeba ids 64 bits, which Tanenbaum's structure could accommodate. Queensland's structure would have grown to 320 bits, but in either case all the fields would have been 32-bit aligned (so hopefully quicker to work with). And it was expected that both memory sizes and network speeds would be significantly increased in the near future, so nobody really was worried about ticket sizes. When Tanenbaum announced 256-bit capabilities for v4, I assumed that dual signatures had lost because everyone previously had agreed that the existing 48-bit signing was insufficient. It didn't seem likely that Queensland's dual signatures would be squeezed into 256 bits. ??? It's all academic at this point.
>I'd also like to have seen how they handled passing the capability >to a surrogate (e.g., how do you interpose a "debugger" agent >without the actor being debugged having to "cooperate", etc.)
Simple: you couldn't. Not that it would have been impossible, but Amoeba didn't provide any way to do it. FLIP made connections based on *private* identifiers and maintained specific client:server pair associations for stateful services. It was possible for a debugger to impersonate the *public* port id of either endpoint (or even both), but it was not possible to break into an existing connection, nor could the debugger steer new connections to itself if the endpoint was already running ... FLIP would simply see the debugger as yet another instance of the requested target. WRT surrogates, I'm not sure what really is the question. You either connect to the surrogate directly and pass the ticket in a message, or name the ticket and stick it in the directory where the surrogate can find it.
>And, the costs as tasks migrate (IIRC, Amoeba really didn't >migrate tasks as much as "picking a suitable INITIAL HOST" >for a particular task)
Yes. Amoeba didn't do task migration out of the box - it simply provided location transparency so that migration services could be added (relatively) easily on top of it. George
Reply by Don Y December 2, 20132013-12-02
Hi George,

On 12/2/2013 5:40 PM, George Neuner wrote:
> Hope you and C had a nice Thanksgiving.
Yup -- homemade pizza! :) I assume "mom" refused all (most) efforts for help?
>> OK, so I went through *my* archive. The latest documentation I >> have is *5.3*! Everything there mentions 128-bit capabilities. >> Textbook (previously quoted), papers, user/system/programming manuals, >> etc. >> >> : >> >> I.e., not only don't I see any *concrete* reference to 256-bit >> capabilities: > > Sorry for the confusion. > > When I played with Amoeba - circa ~1990 - the basic OS had 128-bit > capabilities, but extended capabilities were available as patches. At > that time there were a number of competing versions (with slightly > different structure) providing additional and longer fields > [I actually worked with 2 different ones].
OK.
> I've tried to search some for the patches, but most of the old Amoeba > archives seem to be gone now. When I was in school, there were dozens > of universities using Amoeba.
As I said, I wasn't able to find anything -- I figured "256 bit amoeba" would be about as *vague* as I could devise for a search criteria (not even mentioning "capabilities"!)
> I didn't pay a lot of attention to it after leaving school, but since > Tanenbaum announced that (some version of) extended capabilities were > to be in version 4, I assumed that they had agreed on what they > wanted. From what I can see now, it appears that nothing ever > happened.
So, a learning opportunity is lost. It would have been informative for them to crank out another paper explaining what the problems with the 256 bit implementation were that caused it not to be pursued "formally". I'd also like to have seen how they handled passing the capability to a surrogate (e.g., how do you interpose a "debugger" agent without the actor being debugged having to "cooperate", etc.)
> FWIW: the most up to date version of Amoeba appears to be the Fireball > distribution (http://fsd-amoeba.sourceforge.net). It's based on 5.3 > with a number of additional goodies. However it still doesn't include > any version of extended capabilities.
Yes. I pulled down the sources and see not much has *significantly* changed. Actually, appears the guy working on that release has gone in directions that AST had "belittled" in previous pubs. :>
>>> However, FLIP is special because it is involved in *every* IPC call, >>> so access to the local FLIP server is unconditional and the ticket >>> specifies only whether FLIP should permit the process to talk to >>> remote servers. >> >> I don't see that anywhere. The ticket holder has no idea *where* the >> "server port" mentioned in the ticket resides. It may have resided on >> the local host when it was isssued and since migrated to a remote >> host. The whole point of FLIP was to make all that transparent to the >> user. > > Unless they changed things dramatically, it should be apparent in the > in the code for exec() and in the kernel IPC code. There was an > extended exec call which took flags and an array of tickets to set as > defaults for the child process. > > There wasn't any explicit FLIP "ticket" because FLIP wasn't an > addressable service. IIRC, the same-host restriction was a bit in the > process's local message port identifier (which the kernel provides to > FLIP when the process makes an IPC call).
I can see the "decision" where the "local" branch is invoked.
>>> For each local process, FLIP creates a "FLIP port", yet another UUID >>> which is associated with the process's message port. FLIP locates >>> servers based on their service ids, but because there may be multiple >>> instances, to support stateful servers FLIP maintains associations >>> between clients and servers based on their unique FLIP ports. >> >> "All low-level comunication in Amoeba is based on FLIP addresses. >> Each process has exactly one FLIP address: a 64-bit random number >> chosen by the system when the process is created. If the process >> ever migrates, it takes its FLIP address with it. If the network >> is ever reconfigured, so that all machines are assigned new >> (hardware) network numbers or network addresses, the FLIP addresses >> still remain unchanged." > > When I used Amoeba, FLIP ports were 48-bits, same as server ports. > >> We don't disagree on our understandings of how FLIP is implemented. >> What I *don't* see is any "gating function" that prevents tasks from >> accessing a server that is remote (assuming I have a valid ticket >> bearing that server's "server port" -- which *may* migrate from >> local to remote in the cource of the ticket's lifetime in my context). >> >> I don't see anything that imposes additional "authorization" checks >> on local vs remote transactions in the sources. >> >> So, you're reading from a different play book than me. I just >> want to peek over your shoulder and read along! :> > > It could be something that simply faded away. Tanenbaum and Mullender > famously disagreed about having special support for (semi-)private > workstations and servers. Their groups created early versions of > Amoeba that had different extensions.
<frown> I think what I have to do is extend the "authorizations" that are implied (by the server backing the object) to also provide other "authorizations" for the underlying port/Handle itself. I.e., if I want to *know* who is Holding a particular Handle, then disable the ability to copy and/or propagate that Handle when I give it to the "Holder". Then, I know any activity on that particular Handle *must* be coming from that Holder (because the kernel is involved in the duplication process -- unlike Amoeba's tickets). Dunno. I'll have to see what sorts of problems this presents. And, the costs as tasks migrate (IIRC, Amoeba really didn't migrate tasks as much as "picking a suitable INITIAL HOST" for a particular task) Biscotti, tonight. Have to start tackling the holiday chores :< --don
Reply by George Neuner December 2, 20132013-12-02
Hi Don,


Hope you and C had a nice Thanksgiving.



On Sun, 24 Nov 2013 22:30:56 -0700, Don Y <This.is@not.Me> wrote:

> : > >OK, so I went through *my* archive. The latest documentation I >have is *5.3*! Everything there mentions 128-bit capabilities. >Textbook (previously quoted), papers, user/system/programming manuals, >etc. > > : > >I.e., not only don't I see any *concrete* reference to 256-bit >capabilities:
Sorry for the confusion. When I played with Amoeba - circa ~1990 - the basic OS had 128-bit capabilities, but extended capabilities were available as patches. At that time there were a number of competing versions (with slightly different structure) providing additional and longer fields [I actually worked with 2 different ones]. I've tried to search some for the patches, but most of the old Amoeba archives seem to be gone now. When I was in school, there were dozens of universities using Amoeba. I didn't pay a lot of attention to it after leaving school, but since Tanenbaum announced that (some version of) extended capabilities were to be in version 4, I assumed that they had agreed on what they wanted. From what I can see now, it appears that nothing ever happened. FWIW: the most up to date version of Amoeba appears to be the Fireball distribution (http://fsd-amoeba.sourceforge.net). It's based on 5.3 with a number of additional goodies. However it still doesn't include any version of extended capabilities.
>> However, FLIP is special because it is involved in *every* IPC call, >> so access to the local FLIP server is unconditional and the ticket >> specifies only whether FLIP should permit the process to talk to >> remote servers. > >I don't see that anywhere. The ticket holder has no idea *where* the >"server port" mentioned in the ticket resides. It may have resided on >the local host when it was isssued and since migrated to a remote >host. The whole point of FLIP was to make all that transparent to the >user.
Unless they changed things dramatically, it should be apparent in the in the code for exec() and in the kernel IPC code. There was an extended exec call which took flags and an array of tickets to set as defaults for the child process. There wasn't any explicit FLIP "ticket" because FLIP wasn't an addressable service. IIRC, the same-host restriction was a bit in the process's local message port identifier (which the kernel provides to FLIP when the process makes an IPC call).
>> For each local process, FLIP creates a "FLIP port", yet another UUID >> which is associated with the process's message port. FLIP locates >> servers based on their service ids, but because there may be multiple >> instances, to support stateful servers FLIP maintains associations >> between clients and servers based on their unique FLIP ports. > > "All low-level comunication in Amoeba is based on FLIP addresses. > Each process has exactly one FLIP address: a 64-bit random number > chosen by the system when the process is created. If the process > ever migrates, it takes its FLIP address with it. If the network > is ever reconfigured, so that all machines are assigned new > (hardware) network numbers or network addresses, the FLIP addresses > still remain unchanged."
When I used Amoeba, FLIP ports were 48-bits, same as server ports.
>We don't disagree on our understandings of how FLIP is implemented. >What I *don't* see is any "gating function" that prevents tasks from >accessing a server that is remote (assuming I have a valid ticket >bearing that server's "server port" -- which *may* migrate from >local to remote in the cource of the ticket's lifetime in my context). > >I don't see anything that imposes additional "authorization" checks >on local vs remote transactions in the sources. > >So, you're reading from a different play book than me. I just >want to peek over your shoulder and read along! :>
It could be something that simply faded away. Tanenbaum and Mullender famously disagreed about having special support for (semi-)private workstations and servers. Their groups created early versions of Amoeba that had different extensions. George
Reply by Don Y November 25, 20132013-11-25
Hi George,

On 11/24/2013 3:32 AM, George Neuner wrote:
> On Thu, 21 Nov 2013 09:50:38 -0700, Don Y<This.is@not.Me> wrote: > >> If you needed a special ticket to use the socket service, how did you >> talk to *any* service as you had no knowledge of what was "local" vs. >> "remote"? I.e., that was the appeal of FLIP! > > It probably would be faster to read the Amoeba documentation rather > than to ask me these questions. 8-)
I'd *love* to read them! Anything beyond what I've *already* read, that is! :> (remember, I tend to keep big archives). So, I went looking for more recent documents/sources (my Amoeba archive ends in the mid/late 90's). '"256-bit" Amoeba' seemed like a safe search criteria! This has been an amusing experience! :> The Wikipedia page <http://en.wikipedia.org/wiki/Amoeba_distributed_operating_system> doesn't describe (nor mention!) capabilities but offers a clue: "Each thread was assigned a 48-bit number called its "port", which would serve as its unique, network-wide "address" for communication." I.e., this is consistent with the 48-bit "server port" mentioned in other descriptions of 128-bit capabilities (though nothing about that precludes a larger capability implementation!) In <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.8291&rep=rep1&type=pdf> (forgive the wrap), Tanenbaum et al. claim, in describing Amoeba *3.0*: "The structure of a capability is shown in Fig. 2. It is 128 bits long and contains four fields. The first field is the server port, and is used to identify the (server) process that manages the object. It is in effect a 48-bit random number chosen by the server. Later, when discussing planned changes: "Amoeba 4.0 uses 256-bit capabilites, rather than the 128-bit capabilities of Amoeba 3.0. The larger Check field is more secure against attack, and other security aspects have also been tightened, including the addition of secure, encrypted communication between client and server. Also, the larger capabilities now have room for a location hint which can be exploited by the SWAN servers for locating objects in the wide-area network. Third, all the fields of the new 256-bit capability are now all aligned at 32-bit boundaries which potentially may give better performance." OK, this lends strength to your comment that capabilities were "enlarged" to 256 bits. But wait, there's more! :> For example <doc.utwente.nl/55885/1/andrew.pdf> (Tanenbaum et al. in "The Communications of the ACM", Dec 1990), in describing "the current state of the system (Amoeba 4.0)" claims: "The structure of a capability is shown in Figure 2. It is 128 bits long and contains four fields. The first field is the /server port/, and is used to identify the (server) process that manages the object. ..." Later, it states: "Amoeba 5.0 will use 256-bit capabilities, rather than the 128-bit capabilities of Amoeba 4.0. The larger Check field will be more secure against attack. Other security aspects will also be tightened, including the addition of secure, encrypted communication between client and server. Also, the larger capabilities will have room for a location hint which can be exploited by the SWAN servers for locating objects in the wide-area network. Third, all the fields of the new 256-bit capability will be aligned at 32-bit boundaries, which potentially may give better performance." Sure looks like someone recycled a "paper" :> Did you catch the changes in the versions mentioned in each? ;) OK, so I went through *my* archive. The latest documentation I have is *5.3*! Everything there mentions 128-bit capabilities. Textbook (previously quoted), papers, user/system/programming manuals, etc. Hmmm... perhaps a "consistent typo" <grin> (where have I seen *that* sort of thing before?) Dig through my copy of the sources. From amoeba.h (note am_types.h defines the other types mentioned here without surprises): ------8<------8<------ #define PORTSIZE 6 #include <am_types.h> typedef struct { int8 _portbytes[PORTSIZE]; } port; typedef struct { int8 prv_object[3]; /* becomes an objnum in amoeba 4 */ uint8 prv_rights; /* becomes a rights_bits in amoeba 4 */ port prv_random; } private; typedef struct { port cap_port; private cap_priv; } capability; ------8<------8<------ I.e., a "capability" is *still* 128 bits as of Amoeba 5.3 despite Tanenbaum's published plans to enlarge it "in Amoeba 4.0"! And "in Amoeba 5.0"! OK, maybe there is "something newer" out there. Visit <ftp://ftp.cs.vu.nl/pub/amoeba/> and there's only an "amoeba5.3" directory. So, that's consistent with my most recent entities. And, the documents there seem to be identical to mine. As do the sources. <frown> Are you sure the documents you're referencing don't, for example contain passages like: "The structure of a capability is shown in Figure 2. It is 128 bits long and contains four fields. The first field is the /server port/, and is used to identify the (server) process that manages the object. ..." And, later: "Amoeba *6.0* will use 256-bit capabilities, rather than the 128-bit capabilities of Amoeba *5.0*. The larger Check field will be more secure against attack. Other security aspects will also be tightened, including the addition of secure, encrypted communication between client and server. Also, the larger capabilities will have room for a location hint which can be exploited by the SWAN servers for locating objects in the wide-area network. Third, all the fields of the new 256-bit capability will be aligned at 32-bit boundaries, which potentially may give better performance." <grin> [Actually, I'd enjoy looking at them and chasing down any sources if for no other reason than to enhance my archive! Google hasn't been very helpful to me.] Returning to the Wikipedia page cited above, it mentions: "Development at the Vrije Universiteit was stopped: the files in the latest version (5.3) were last modified on 12 February 2001." <frown> OK, so I guess my archive is up-to-date in light of that. It goes on to say: "Recent development is carried forward by Dr. Stefan Bosse at BSS Lab." From this, Google pointed me at <http://fsd-amoeba.sourceforge.net/start.html> But, grep(1)-ing the documents there seem to suggest capabilities are still 128-bits. [I've downloaded the sources available there so I can check in case this is a case of the documents simply not being updated to reflect changes in the implementation. I suspect that *won't* be the case for something as fundamental as this!] I.e., not only don't I see any *concrete* reference to 256-bit capabilities: "In the original version yes ... later they went to a 256-bit ticket to include more end-point information and better crypto- signing." "Yes. However, the enlarged capability was an improvement over the original because it carried information on client(s) authorized to use the capability." "That's part of the reason Amoeba used wide tickets. [1st version used 80-bits without the crypto-signing field, 128 bits in all. 2nd version capabilities were 256 bits]." "Later versions of the capability system widened tickets to include an authorized user/group ID field protected by a 2nd crypt signature. [And also enlarged the rights field.]" "That's only true of the original implementation. Later versions expanded the ticket to include an "authorized user" field which variously could be a process, host or host:process identifier, or a admin level user/group id. The user field was protected by a 2nd crypt signature. This enabled servers to restrict who could use a ticket." "About 4 times now I have told you that Amoeba could restrict the user of a ticket. No, the kernel doesn't know who has tickets or how many copies exist. Yes, a server can tell who is presenting it a ticket and decide whether the user is authorized." So, to *what* are you refering that gives you this extra information? I *have* to "ask [you] these questions" given that I can't "read the Amoeba documentation" that apparently contains these references! Share! :>
> Hopefully the following will all be clear. > --- > > I covered already how every process has a set of tickets - either > default or explicitly acquired - that authorize it to make particular > "system calls". Technically Amoeba has only a handful of kernel calls > which support message based IPC, however messaging to/from the > standard servers largely is hidden by a library that presents a > Un*x-like "system" API. > > FLIP itself is a service for which every process needs a ticket.
That's not the case in the documentation I've cited! In the second paper mentioned at the outset, it also states: "[Mach's CoW usage in IPC] Amoeba does not do this because we consider the key issue in a disributed system to be the communication speed between processes running on /different/ machines. That is the normal case. Only rarely will two processes happen to be on the same physical processor in a true distributed system, especially if there are hundreds of processors; therefore we have ou a lot of effort into optimizing the /distributed/ case, not the /local/ case. This is clearly a philosophical difference." It hardly seems likely that they would introduce extra mechanism for processes to access remote services (i.e., any capability whose server port is not found to be local) when they *expect* this to be the normal case. I.e., requiring a capability to use the network services as you mentioned previously. To wit, Tanenbaum's _Distributed Operating Systems_ text, section 7.5.1 explaining RPC primitives: "The RPC mechanism makes use of three principal kernel primitives: 1. get_request -- indicates a server's willingness to listen on a port 2. put_reply -- done by a server when it has a reply to send 3. trans -- send a message from client to server and wait for the reply The first two are used by servers. The third is used by clients to /transmit/ a message and wait for a reply. All three a true system calls, that is, they do not work by sending a message to a communication server thread. (If processes are able to send messages, why should they have to contact a server for the purpose of sending a message?)" Instead, the "server port" is extracted from the "ticket" and "resolved" in the RPC code (which may, in fact, run as a separate kernel *thread*/process --- but access to it is unanimously granted!). [The "Bosse" version gives capability based control over the TCP/IP stack -- but makes no mention of requiring additional capabilities to use FLIP for "typical" IPC (which is actually RPC in the "normal case" from the quote immediately above.)]
> However, FLIP is special because it is involved in *every* IPC call, > so access to the local FLIP server is unconditional and the ticket > specifies only whether FLIP should permit the process to talk to > remote servers.
I don't see that anywhere. The ticket holder has no idea *where* the "server port" mentioned in the ticket resides. It may have resided on the local host when it was isssued and since migrated to a remote host. The whole point of FLIP was to make all that transparent to the user.
> FLIP isn't part of the kernel, it is a separate task. However, FLIP is > tightly integrated with the kernel and has direct access to certain > kernel functions. If you're familiar with Minix, in that model FLIP > would exist as a "driver task". > > Each process has a bi-directional message "port" by which it talks to > FLIP. The message port can have associated with it an optional > service identifier [which is a network wide UUID]. Every program > which will wants to be a server requires a service identifier in order > to be located. Every instance of a particular service will use the > same identifier, which also is used as the port-id in their service > tickets. > > The kernel IPC calls copy (relatively) small request/response messages > between the FLIP server and these process ports. > > All other communication occurs *within* FLIP. Requests may specify > optional data buffers which FLIP memmaps for direct access or remaps > for local handoff. For remote messages FLIP copies data between the > local process and the remote host. FLIP itself implements a large > datagram service within the Amoeba network - foreign transport > protocols such as TCP/IP are implemented at higher levels using a > normal service. [The network driver is separate from FLIP.] > > So how is it done? > > For each local process, FLIP creates a "FLIP port", yet another UUID > which is associated with the process's message port. FLIP locates > servers based on their service ids, but because there may be multiple > instances, to support stateful servers FLIP maintains associations > between clients and servers based on their unique FLIP ports.
"All low-level comunication in Amoeba is based on FLIP addresses. Each process has exactly one FLIP address: a 64-bit random number chosen by the system when the process is created. If the process ever migrates, it takes its FLIP address with it. If the network is ever reconfigured, so that all machines are assigned new (hardware) network numbers or network addresses, the FLIP addresses still remain unchanged." I.e., each "local" process registers itself with the FLIP layer. Any *local* message goes through FLIP to see that the destination "port" (FLIP address) resides on the local host. Similarly, if the FLIP layer has "been in contact" with some remote FLIP address(es), if caches the information needed to reconnect with them in the future. If ever it is unable to contact the expected (or, unknown) host, it resorts to a (series of ever widening) broadcast queries. This is no different from how much of IP works. Except for the added layer of there mobile, "virtual" addresses (ports). [I spent a lot of time wading through the FLIP implementation as the same sorts of issues have to be addressed in *any* distributed OS. E.g., Amoeba's portable "service ports" are similar to Mach's *transferable* ports (esp receive rights). And, as it is far more common for a Mach port to be "moved" than an Amoeba *process* (to which the FLIP address is bound!) the costs of locating and tracking such entities across the system is of greater concern in a Mach-based approach! Broadcast queries, self-publishing, periodic tokens, etc. Lots of ways to deal with systemwide objects WITHOUT a central "control". But all of them have drawbacks and consequences so picking what's right for a particular *application* (no, not OS!) is tricky]
> The FLIP port globally identifies a particular process within the > Amoeba network and it can be used to track the process through host > migrations (if they occur). > > FLIP implements a distributed name service: it locates servers by > looking up the port id in the service ticket associated with the > client request. If the service is unknown, FLIP broadcasts a name > resolution query specifying the port id to its FLIP peers to locate > servers within the network. Replies (if any) identify the hosts and > FLIP ports of the server processes, which then are associated with the > service's port id entry. > > Once a server's FLIP port is known, FLIP copies messages between the > local client and the server. If the server also is local, FLIP copies > the client's message directly. If the server is remote, FLIP packs > the message into a datagram addressed to the server's FLIP port and > sends it to its peer on the server's host. > [Analogous events happen for the server->client response.] > > A send to a remote process may fail because the process has migrated > (the old host will say it doesn't exist). If this happens, FLIP > issues a new name resolution query specifying the specific FLIP port > of the target process to try to find it again. If the process can be > located, FLIP will retry the send. > [Processes cannot migrate while sending a message, so migration of the > remote process will never be the cause of a receive failure.] > > Name entries for local services (processes) are removed when the last > instance terminates. Entries for remote services are kept separately > and eventually time out and are removed if not used.
We don't disagree on our understandings of how FLIP is implemented. What I *don't* see is any "gating function" that prevents tasks from accessing a server that is remote (assuming I have a valid ticket bearing that server's "server port" -- which *may* migrate from local to remote in the cource of the ticket's lifetime in my context). I don't see anything that imposes additional "authorization" checks on local vs remote transactions in the sources. So, you're reading from a different play book than me. I just want to peek over your shoulder and read along! :>
>>> To do much of anything, a Mach server has to register a public service >>> port with send rights - which any task in the network can scan for and >>> try to connect to. Aside from limiting the count of available send >>> rights to the port, there is no way to prevent anyone from connecting >>> to it. >>> >>> Only *after* determining that the connection is unwanted can the >>> server decide what to do about it. Using MIG didn't affect this: you >>> couldn't encode port ids into the client using IDL - the only way for >>> a client to find the server was to look up its registered public port. >> >> No. That's the way Mach was *applied* -- because they were looking >> towards "UNIX as an application". *Obviously* (?) you need to be >> able to find services! >> >> Or, *do* you? >> >> What's to stop me from creating a task with the following namespace: >> CurrentTime >> Display >> AND NOTHING MORE! > > Nothing stops *your system* from doing anything it wants ... however, > we _were_ talking about Mach.
Ah, but there's no practical difference between the two in that regard! :> Mach "out of the box" can do exactly the same thing! It's just a matter of which port you pass to each task as you create the task that defines it's "namespace". I.e., instead of using a SINGLE, SHARED, GLOBAL namespace "for all", you could just as easily build one for each task. Or, different ones for different *types* of tasks. I.e., it is trivial to pass (send rights) fot *different* ports to each task on instantiation and have the receive rights for each of those handled by the same "netmsgserver". But, as that *NAME* server would be able to identify on which port any particular name "lookup()" request (IPC/RPC) was issued, it could tesolve the name in a context DEFINED FOR and associated with the task(s) that have send rights *to* that particular port! E.g., all "user" tasks could have "/dev" removed from their namespaces simply by eliding those names from the "tables" that you build in the netmsgserver to service the ports (send rights) *given* to "user tasks". But, the UNIX-orientation crept in, yet again. UNIX has a single shared namespace so why not implement a single shared namespace?! :< It apparently never occured to them that separate namespaces are a powerful tool! E.g., "UNIX namespace", "DOS namespace", "task 1's namespace", etc. And, all *might* map to similar/shared objects, concurrently! "COM1:" in the DOS namespace maps to the same device that "/dev/cuaa0" maps to in the UNIX namespace -- neither of which exist in task 1's namespace because he's not supposed to be using that sort of I/O... [C's email machine has died. I'll have to fix it tonight lest I get the pouty face come tomorrow! (sigh) Sure would be nice NOT to have to be an IT department!! :< ]
Reply by George Neuner November 24, 20132013-11-24
Hi Don


On Thu, 21 Nov 2013 09:50:38 -0700, Don Y <This.is@not.Me> wrote:

>If you needed a special ticket to use the socket service, how did you >talk to *any* service as you had no knowledge of what was "local" vs. >"remote"? I.e., that was the appeal of FLIP!
It probably would be faster to read the Amoeba documentation rather than to ask me these questions. 8-) Hopefully the following will all be clear. --- I covered already how every process has a set of tickets - either default or explicitly acquired - that authorize it to make particular "system calls". Technically Amoeba has only a handful of kernel calls which support message based IPC, however messaging to/from the standard servers largely is hidden by a library that presents a Un*x-like "system" API. FLIP itself is a service for which every process needs a ticket. However, FLIP is special because it is involved in *every* IPC call, so access to the local FLIP server is unconditional and the ticket specifies only whether FLIP should permit the process to talk to remote servers. FLIP isn't part of the kernel, it is a separate task. However, FLIP is tightly integrated with the kernel and has direct access to certain kernel functions. If you're familiar with Minix, in that model FLIP would exist as a "driver task". Each process has a bi-directional message "port" by which it talks to FLIP. The message port can have associated with it an optional service identifier [which is a network wide UUID]. Every program which will wants to be a server requires a service identifier in order to be located. Every instance of a particular service will use the same identifier, which also is used as the port-id in their service tickets. The kernel IPC calls copy (relatively) small request/response messages between the FLIP server and these process ports. All other communication occurs *within* FLIP. Requests may specify optional data buffers which FLIP memmaps for direct access or remaps for local handoff. For remote messages FLIP copies data between the local process and the remote host. FLIP itself implements a large datagram service within the Amoeba network - foreign transport protocols such as TCP/IP are implemented at higher levels using a normal service. [The network driver is separate from FLIP.] So how is it done? For each local process, FLIP creates a "FLIP port", yet another UUID which is associated with the process's message port. FLIP locates servers based on their service ids, but because there may be multiple instances, to support stateful servers FLIP maintains associations between clients and servers based on their unique FLIP ports. The FLIP port globally identifies a particular process within the Amoeba network and it can be used to track the process through host migrations (if they occur). FLIP implements a distributed name service: it locates servers by looking up the port id in the service ticket associated with the client request. If the service is unknown, FLIP broadcasts a name resolution query specifying the port id to its FLIP peers to locate servers within the network. Replies (if any) identify the hosts and FLIP ports of the server processes, which then are associated with the service's port id entry. Once a server's FLIP port is known, FLIP copies messages between the local client and the server. If the server also is local, FLIP copies the client's message directly. If the server is remote, FLIP packs the message into a datagram addressed to the server's FLIP port and sends it to its peer on the server's host. [Analogous events happen for the server->client response.] A send to a remote process may fail because the process has migrated (the old host will say it doesn't exist). If this happens, FLIP issues a new name resolution query specifying the specific FLIP port of the target process to try to find it again. If the process can be located, FLIP will retry the send. [Processes cannot migrate while sending a message, so migration of the remote process will never be the cause of a receive failure.] Name entries for local services (processes) are removed when the last instance terminates. Entries for remote services are kept separately and eventually time out and are removed if not used.
>> To do much of anything, a Mach server has to register a public service >> port with send rights - which any task in the network can scan for and >> try to connect to. Aside from limiting the count of available send >> rights to the port, there is no way to prevent anyone from connecting >> to it. >> >> Only *after* determining that the connection is unwanted can the >> server decide what to do about it. Using MIG didn't affect this: you >> couldn't encode port ids into the client using IDL - the only way for >> a client to find the server was to look up its registered public port. > >No. That's the way Mach was *applied* -- because they were looking >towards "UNIX as an application". *Obviously* (?) you need to be >able to find services! > >Or, *do* you? > >What's to stop me from creating a task with the following namespace: > CurrentTime > Display >AND NOTHING MORE!
Nothing stops *your system* from doing anything it wants ... however, we _were_ talking about Mach. George
Reply by Don Y November 22, 20132013-11-22
Hi George,

On 11/21/2013 5:16 AM, George Neuner wrote:
> On Tue, 19 Nov 2013 15:13:07 -0700, Don Y<this@isnotme.com> wrote: > >> Actually, Amoeba *does* GC! Each server is responsible for >> discarding "unreferenced" objects whenever a GC cycle is >> invoked for/on that server. >> >> The difference is, Amoeba doesn't know where all the "references" >> (handles/tickets) are. So, it has to, instead, implement GC >> by noticing which objects haven't been *referenced* (actively >> operated upon). >> >> So, a ticket holder has to periodically tickle the object for >> which he's holding the ticket -- lest the server decide the >> object is no longer being referenced ("of interest") to >> anyone. > > Not exactly.
Argh! Had to go wade through notes -- which then coerced me to dig through boxes of books that *HAD* been on their way to be donated. Now, of course, having "re-noticed" their actual titles, I will be strongly tempted to start cherry picking through the boxes... and the cycle will repeat. :< Anyway, _Distributed Operating Systems_ (Tannenbaum -- of Amoeba fame) describing "Objects and Capabilities in Amoeba" (section 7.2). "7.2.1 Capabilities" covers the idea of capabilities (128b) and the fields therein (server port, object, rights, check). "7.2.2 Object Protection" describes cryptographic seal and how impossible to forge added rights. "7.2.3 Standard Operations" deals with operations (methods) that can be invoked on objects. Fig 7-5 tabulates "The standard operations valid on most objects". These are: Age perform a single GC cycle (!) Copy duplicate the object Destroy destroy object and reclaim storage Getparama get params associated with server (backing the object) Info ASCII string describing the object Restrict Produce a new *restricted* capability for the object Setparams set params associated with server Status current status information from the server Touch pretend the object was just used Second paragraph begins: It is possible to create an object in Amoeba and then lose the capability, so some mechanism is needed to get rid of old objects that are no longer accessible. The way that has been chosen is to have servers run a garbage collector periodically, removing all objects that have not been used in /n/ garbage collection cycles. The AGE call starts a new garbage collection cycle. The TOUCH call tells the server that the object touched is still in use. I.e., the system has no way of knowing where all outstanding tickets for an object may be. So, it can't try to *resolve* references and use that to identify EVERY object that remains of interest. Even if the kernel were to grep() each process's memory, it would never be able to say, "Aha! Here's another ticket! Let's see what it REFERENCES and MARK that object as STILL OF INTEREST." There's no way to *recognize* a ticket in memory -- it's just bits! So, instead, Amoeba has servers maintain a time to live field for each object. Whenever an object is acted upon, the TTL is reset. An explicit TOUCH operation also resets it -- though without doing anything "actively" to the object (sort of a NOP -- this is what I previously erroneously called "notice") Whenever an AGE operation is invoked on an object, the TTL decreases by one unit. Once it reaches "0", the server managing the object (i.e., the only entity that is aware of things like TTL) can delete the object. No one has "noticed" it during the course of it's TTL (i.e., those "/n/" GC cycles (invocations of the AGE operation). So, if an actor holds a ticket that references an object (regardless of what type of object it may be) and just *sits* on it, when some *other* agency has invoked the GC on the server that manages said object those /n/ times, the object evaporates! "Use it or lose it". Note that the actor has no idea that /n/-1 GC cycles may have been performed in the time since he acquired the ticket. (remember, each object's TTL can be different at any instant in time -- it's not like *all* "foo" objects will expire at the same instant or GC cycle so no way for the system to alert everyone of an upcoming sweep: "TOUCH anything you want to keep cuz there's a GC cycle up ahead!".) In practical terms, this means that you have to artificially use every object (ticket) that you hold "often enough" to prevent the system (actually, the individual servers backing each of those objects) from deciding that no ne is interested in the object any longer and GC'ing it. So, for example, if you have stderr wired to "something" and haven't had any "errors", so far, in your execution (i.e., no need to write() to stderr), you risk having the object that your stderr is attached to (file, pty, etc) being GC'ed *before* you eventually have a need to use it! UNLESS you deliberately tickle that object periodically. Remember, if the object you are tickling is backed by a server on another node/host, that "tickle"/TOUCH means an RPC across the network! For *every* object that you want to "keep alive". In addition to this waste, how can an application designer (resource consumer) RELIABLY design to protect against such ASYNCHRONOUS, UNNOTIFIED revocations when designing his app? What is /n/ for EACH particular type of object? How often will the server for each type of object be called upon to perform a GC cycle (AGE)? How much time will my application spend NOT BLOCKED (i.e., able to execute code) in that interval? To be "safe", do I need to write my code like: payroll() { ... wages = hours * rate; tickle_objects(); benefits = lookup(my_options); tickle_objects(); taxes = (wages - benefits) * tax_rate; tickle_objects(); net = wages - (benefits + taxes); output(net); ... } Tanenbaum continues: When objects are entered into the directory server, they are touched periodically, to keep the garbage collector at bay. I.e., for *named* objects and a server whose role it is to maintain that (name, object) tuple, a process periodically runs through that service TOUCHing every object therein. I.e., every named object is accessed continuously just to make sure it doesn't evaporate. Who handles anonymous objects? Or, objects that may not need to be accessible beyond some group of cooperating actors?
>> Amoeba had a "notice" (can't recall the name of the actual >> method) operation for each object for this express purpose.
"TOUCH"
> Amoeba's distributed filesystem is comprised of peer/proxy servers. > When a non-local file is opened, the local server obtains a copy of > the file's capability to accelerate future accesses. Then it acts as > a proxy to the remote server, performing access validations locally > and relaying requests/data over a trusted peer channel. > > The file servers time out and remove cached non-local capabilities if > they aren't used. They do NOT time out capabilities for their own > locally hosted objects.
It's got nothing to do with local v remote. In Amoeba, you simply have no way of knowing if there are outstanding references to an object. You (Amoeba implementor) have two options on how you handle this: - *expect* any object that has not explicitly destroyed to be of some interest to someone, somewhere. - expect people interested in objects to periodically RE-express that interest and, after a time, decide that anything for which an interest ha snot been expressed (TOUCHed or otherwise acted upon) is of no interest to anyone! The latter is Amoeba's approach as the former would result in "capability leakage" -- objects persisting long after the capabilities involving them have passed -- with no way of harvesting these. But, as you can see, it means extra (network!) traffic as a certain level of "interest" must be shown in EVERY object in the distributed system lest some objects be PREMATURELY harvested! And, still provides no guarantee that this premature harvest won't occur! No way for an application designer to guarantee that he has been "interested enough" in the objects for which he may be the sole ticket-holder! Nor any way for him to be sure he has had enough "CPU time" to *express* that interest! I can see apps being "tuned" (effectively) as system load changes. E.g., "Hmmm... we'll have to increase /n/ as some tickets are timing out too soon and we can't afford to rewrite all those apps that *use* those objects!" Sort of like arbitrarily increasing a thread's stack each time you get a SIGSEGV... always *hoping* this latest increase is "big enough". In Mach's case, the kernel (kernelS) know where all outstanding port rights (send/send-once) are. So, they can tell a server when no one is interested in a particular object (receive right) any longer. As soon as that interest goes away (as soon as the last send right is destroyed). Mach burdens the kernel with this sort of thing so the actors don't have to worry about it. So they don't have to be "tuned" to a particular server implementation (there's no /n/!)
> AFAIK, the filesystem is the only included peer/proxy service - all > the others either are host local or are simply peer. I'm not aware > that any of the other services time out capabilities.
And, of course, time for my morning tea! :>
Reply by Don Y November 21, 20132013-11-21
Hi George,

["pro bono" day so I've got to get my *ss out the door, RSN]

On 11/21/2013 5:16 AM, George Neuner wrote:
> On Tue, 19 Nov 2013 16:49:10 -0700, Don Y<this@isnotme.com> wrote: > >> OK. But, did the *kernel* make the decision as to whether to >> pass the message (IDL) on to the service (encoded in the >> ticket) based on an examination of that field? Or, did the >> kernel pass it on to the service and rely on the *server* to >> decide "You aren't the right person"? (in addition to >> "You don't have the right permission(s)"?) > > Yes and no. Remember, to make ANY system call requires a ticket. > > Normally, a process is created with a set of default tickets that > enable it to access basic services (filesystem, network, etc.). The > default tickets are held by the kernel and can be chosen by the parent > when the process is created. > > The default ticket for any particular call is assumed unless the > process explicitly presents a different one when the call is made. > > However, the kernel IPC mechanism doesn't validate the message other > than to check that the server address on the ticket is good. E.g., if > the process has no ticket for the socket service,, it will be > prevented from even trying to contact the socket service.
Ticket encodes "server port" in a virtual sense. I.e., for a "name service" analogy, the "server" portion would be "DNS" (not a specific IP address). The system was responsible for locating and tracking which "processor" was currently "hosting" that service. (i.e., DNS might be "29" -- today! And, running on 10.10.2.27 -- NOW!) If you needed a special ticket to use the socket service, how did you talk to *any* service as you had no knowledge of what was "local" vs. "remote"? I.e., that was the appeal of FLIP! I.e., the kernel had to invoke the RPC if the service port IN YOUR TICKET was located on a remote host. (recall Amoeba used smallish processors originally. 16MB nominal size physical memory and NO paging!)
>> A [Mach] "send once" right is a send right that is automatically >> destroyed by the kernel when used. I.e., send *one* message, >> no more. > > You can do similar with numbered use tickets/capabilities in Amoeba. > > Everything you can think of has an implementation analogue in either > system. You're just hung up on kernel enforcement.
Exactly! -------------------------^^^^^^^^^^^^^^^^^^ You could implement some silly VM scheme whereby any task (thread) could write anywhere in physical memory -- and, give the owner of that piece of memory the power to allow or inhibit the write. This would allow you to implement protected memory domains whereby task A can't stomp on task B's memory. It would give you an even finer grained (i.e., BETTER!) scheme than current systems use! A task could opt to let another task write on one *byte* and not the byte next to it! You could share objects at whatever granularity you choose! BUT, IT WOULD REQUIRE THE "owning" TASK TO PROCESS EACH INCOMING WRITE ATTEMPT! If tasks all were well-behaved, not a problem -- each write attempt would be a DESIRED one! OTOH, a rogue/adversary could intentionally sit there doing "bad" things. Your memory integrity won't br compromised because you were implemented correctly. But, he's SPENDING YOUR DIME! His actions are causing you to have to burn cycles invalidating his accesses. You can't "block" his actions (in this hypothetical model). So, while it is fine-grained and *could* give you great performance advantages in a BENEVOLENT environment, it proves to be a vulnerability in an environment where adversaries (or, just "buggy" programs) reside. I can put a "perfect" firewall on my home internet connection. But, that doesn't stop adversaries from consuming my incoming (and, as such, outgoing) bandwidth! If that firewall executes *in* my host, then I am burning CPU cycles AT SOMEONE ELSE'S BIDDING.
> E.g., you could design an Amoeba based system where all the system > capabilities were held by [or accessible to] a "validation server" to > which all client calls are directed. The validation server would > authenticate the client's ticket and then hand off the "connection" to > the appropriate server.
Perfect! *If* that validation server executes using the *client's* resources! I.e., you present a (bogus) credential and *it* executes in your time slot (e.g., as a library instead of a server). So, if you want to waste YOUR time presenting bogus credentials, fine. You don't tie up network resources or resources in the object's server just to invalidate your request. *That* is my goal. You and only you pay for your actions -- whether they are careless mistakes or deliberate attacks! I.e., you can't "steal" from the system. Note any *server* that is used to do this can potentially be overwhelmed by "non-benevolent" actors presenting it with bogus credentials KNOWING that the server will have to spend CPU cycles (that it *could* have spent on other, LEGITIMATE services for "benevolent" actors!) "As is", I don't see any way of doing this in Amoeba. It was *intended* to have the servers validate tickets/capabilities -- so the kernel didn't have to understand "what they meant", just "where to send them".
>> Permissions aren't a "bit set" that >> you can add to or subtract from. I.e., there's no limit to >> the number of individual permissions implemented by a single >> "Handle". Nor do the permissions associated with one Handle >> have to be the same as the set supported for some other Handle >> (of the same object type). > > Permissions in Amoeba aren't a "bit set" either ... unless you want > them to be. You have a 32 bit field in the ticket to use however you > want. The field can represent a bit map, or an enumerated value, or be > divided into multiple fields. > > If you don't need to validate ticket holders, you've got another 96 > bits in the v2 user id and 2nd signature field to play with. > > There is no *practical* limit to the set of privileges an Amoeba > capability can administer or that a ticket can claim.
In my scheme, the permissions are implied *in* the Handle. If the server backing a particular Handle is willing to implement a set of N methods for *that* Handle, then the Holder has those permissions. If it chooses to only implement M methods -- possibly even *different* methods -- for another Handle, there is no inconsistency. Everything has a context and only makes sense in that context.
>>> [Same model as Amoeba: first connect, then accept or reject.] >> >> The NORMA server made ports transparent. You didn't know >> whether an IDL was going to result in an "IPC" or an "RPC". >> You just "talked to a port for which you had send right(s)" >> and the kernel did the rest. >> >> No separate permission required to use the network. > > That's not my point. > > To do much of anything, a Mach server has to register a public service > port with send rights - which any task in the network can scan for and > try to connect to. Aside from limiting the count of available send > rights to the port, there is no way to prevent anyone from connecting > to it.
No. That's the way Mach was *applied* -- because they were looking towards "UNIX as an application". *Obviously* (?) you need to be able to find services! Or, *do* you? What's to stop me from creating a task with the following namespace: CurrentTime Display AND NOTHING MORE! Further, the Handles that I endow the task with are: (CurrentTime, read) (Display, write) I.e., even though the IDL for the "Time" object supports a "set_time()" method, the task can't use it (it can *try* to invoke it but the server that backs the Time object will return NO_PERMISSION if it does! This task just spends it's life running: while(1) { now = get_time(); // uses CurrentTime segments = decode_7segments(now); display(segments); // uses Display }
> Only *after* determining that the connection is unwanted can the > server decide what to do about it. Using MIG didn't affect this: you > couldn't encode port ids into the client using IDL - the only way for > a client to find the server was to look up its registered public port.
But the client doesn't have to even have access to a particular server's public port! E.g., there is no reason you ("application") should have access to the device driver for the disk subsystem. Why should I even let you try to *find* it? In *your* namespace (that YOUR creator sought fit to establish KNOWING WHAT YOU SHOULD BE RESPONSIBLE FOR DOING), there is no entry called "DiskSubsystem"! Because there is no entry, you can scream all you want but never get a message to that server! No connection possible! If you *should* have the ability to connect to a server (e.g., the server backing the Time object, above), *it* decides whether to allow particular requests (method invocations) based on "permissions" that have been associated with the "Handle" (incoming port) on which your messages arrive. YOU CAN BE SEEN TO ABUSE THIS. Abuse is something that a server determines -- not the client *nor* the system! 500 attempts to write() a file that has been closed may not be abuse. An attempt to turn off the system's power, OTOH, *may* be! (i.e., you have permission to EXAMINE the current power status: on, battery, off -- not change it! Your attempt to do so indicates a significant bug or a deliberate attack!) In that event, the server can pull the plug on your Handle -- i.e., kill the port on which YOUR requests are received. If you manage to acquire another Handle, it can kill the port FROM WHICH your Handles were issued (even if some other task actually was responsible for creating them and passing them on to you -- an accomplice? Or, just someone who "trusted you more than they should have!"). The point is, the system can take steps to isolate your potential for damage. Can prevent you (eventually) from abusing resources.
>>>> The only way for any of us to know a ticket (capability) has >>>> been revoked is to try to *use* it. If I use mine or you >>>> use yours, we get told THAT capability has been revoked. >>>> But these other (nameless) two guys in my example have no idea >>>> that this has happened (unless one of us knows to tell BOTH >>>> of them). >>> >>> They get told when they try to use it. >>> >>> That's an administrative issue which has little to do with mechanism. >>> In your system, revoked ports just evaporate leaving clients equally >>> clueless unless *somebody* deigns to tell them why. >> >> No. There are two port-related "notification mechanisms" in Mach >> (i.e., concerned with things that happen in the normal course of >> a port's life). >> >> When the last "send right" *to* a particular port dies (is destroyed, >> etc.), a "No More Senders" notification is delivered to the task >> holding the receive right. I.e., no one is interested in the >> object that you represent any longer. THERE IS NO WAY FOR ANYONE >> TO REFERENCE IT (other than you!). Please feel free to deinstantiate >> it and GC it's resources. > > That tells the server all existing clients have disconnected ... it > does NOT tell new clients they can't connect.
Ah, but it does! ALL THE REFERENCES TO THE OBJECT ARE GONE! The object still exists IN THE SERVER, but no one can *talk* to "it" (the representation of the object) any more! I.e., it's like: object = &foo; ... object = NULL; (or, some random number) *object = .... won't work (at least, not on *foo*!) So, why *keep* foo around if no one can access it?
>> When the receive right to a port dies (i.e., when the server that >> is handling the incoming messages SENDed to that port dies or >> destroys the port), a "Dead Name" notification is sent to holders >> of send rights. I.e., the object that you were referencing >> no longer exists. Don't bother trying to access it! > > This tells the server or client the other side has disconnected. It > does not tell them why.
It *can* tell the clients that the *server* is no longer backing the object (i.e., the object no longer exists). Mach used to have a "dead name notification" whereby all actors holding ports (Handles) that referenced that port (object) were notified (sent an exception) of the death. But, this uses a lot of resources and is seldom necessary. I.e., why not just wait for the actors to "discover" this fact when they try to operate on that corresponding "port"? (the port is *marked* as "dead" but the task isn't "informed" of the death). Imagine some well known port dying and having to notify hundreds of actors of this fact. Chances are, most will say, "Gee, that's too bad! Where should I send the FLOWERS??" I.e., there's usually nothing they can DO about it. All you've told them is that, IN THE FUTURE, when/if they try to contact that port/Handle/object, they will receive a DEAD_NAME result in whichever method they invoke. OTOH, for cases where it is important to notify a "Holder" of this sort of "revoked capabilty", the server that was holding that "receive right" (i.e., the port that just died) can *choose* to asynchronously notify the actor on the other end. The actor on the other end can also choose to ignore this notification. AND, things still work "predictably" in each case! If/wehn you try to access the "dead name", the method fails. Too bad, so sad.
>>> Your clients don't know their port privileges have been revoked until >>> they try to use their handles. Someone could be sitting on a useless >>> handle for hours, days or weeks (how long since last reboot?). How is >>> that *substantively* different from a cached ticket in Amoeba? >> >> Above. > > You assume that both tasks [server and client] have implemented > notify_server and do-mach-notify-<whatever>. Unless both sides are > using MIG, you can't assume any notifications will be sent, received > or handled - it isn't *required*.
The kernel does the no-more-senders notification. Of course, the server doesn't have to *use* this information. If it has plenty of resources, it might just choose to mark the port and GC it later. Or, it might decide to free all the resources associated with backing that object. E.g., if the object is a file and it has writes pending, it might mark the buffers to be flushed, then return them to the heap. If it has read-ahead in anticipation of future read() methods, it can just drop that memory (because no one can send read() methods to that object any more!) Dead names have more flexibility. In either case, kernel just provides means to tell folks what is happening. Amoeba can't do this because it doesn't know where tickets are "hiding".
> If a task doesn't implement the relevant notification handler, it > won't know anything has happened until it gets an error trying to use > the port.
For a "dead name", yes, exactly. If a task can *tolerate* not knowing until that point, then why bother *telling* it? You could also have tasks that *never* try to talk to an object even if they have been GIVEN a Handle to it! E.g., if you never write to (your) stderr, what do you care if the file (or process!) it represents has gone away?
>>>> In mach, deleting the port (Handle) deletes all instances of it. >>>> And, since the kernel knows where those instances are, those >>>> Holders can be notified when that happens. >>> >>> Only if they were coded respond to the appropriate signal. And Mach >>> didn't allow signaling a task on a remote host. >> >> If a task doesn't care to deal with "port death", it doesn't have >> to. If it is important to the task's proper operation, it will! > > You're assuming again.
I *said* so! :> "If a task doesn't care to deal with 'port death'..." OTOH, if it *does*, then there needs to be a mechanism whereby the task can be *told* about this -- because it can't "see" what is happening to a port unless it deliberately tries to "use" it.
>>>> The "BSDSS" (Single Server). >>> >>> Was it really the SS version? >> >> Bad choice of names. > > Ok. I misunderstood you to mean it was BSD's single user kernel - not > simply Mach's "single server" model.
BSDSS was an old BSD (license required) kernel ported to run basically as a single Mach "task". I.e., slide Mach *under* an existing BSD UNIX. You gain "nothing" -- except the ability to run other things (even other OS's!) ALONGSIDE that "UNIX"! E.g., you could run BSDSS and POE (DOS server) on the same hardware at the same time -- not "DOS under BSD". Mach-UX was a more modern, less encumbered, kernel running in much the same way. Recall, about this time, the USL lawsuits were making life difficult for the UN*X clones. Lites was a similar project. Mach-US was the "application" that set out to illustrate why the mk approach would be "better" for systems than the monolithic kernel approaches had been, historically. It decomposed "UNIX" into a bunch of different "services" -- typically executing in UserLand. Then, tied them together *via* the microkernel -- instead of letting them rid in a single container atop it (as the single servers had). Unfortunately, Mach-US was too late and folks had already moved on. Trying to retrofit a reasonably mature system (UN*X) into a totally different framework (mk) was a losing proposition from the start. *Obviously*, the mechanisms developed *in* UNIX to handle <whatever> had been tweeked to fit it's idea of how a kernel should be built. To realize any significant advantages, you'd have to rethink what a "UNIX" should *be* -- not what it *is*!
>> But, I'm *reasonably* sure "systems" will be more physically >> distributed. More processors. etc. And, forcing people to >> use things like Berkeley sockets for all those comms is like >> forcing a sprinter to wear scuba flippers! (Limbo/Inferno >> was the final piece of the puzzle to fall into place. Many >> of its ideas map naturally to this framework -- though with >> an entirely different implementation! :< Now, I just have >> to beat it all into submission! :> ) > > I really don't like network agnosticism. It simplifies coding for > simple minded tasks, but it makes doing real time applications like > audio/video streaming, and remote monitoring/control a b*tch because > you don't have visibility to adjust to changing network conditions. > > I hate RPC for the same reason. I often use loopback connections to > communicate between co-hosted tasks. At the very least, using a > network API reminds me that the tasks may, in fact, be separated by a > network. Sometimes it doesn't matter, but sometimes it does.
I'm treating the network in much the same way that PC designers treat the PCI/memory busses. Could you design a "centralized" (non-networked) system with predictable responses if anyone could open the machine and add any BUS MASTERING hardware they wanted? :> How could you reliably know when/if you could access particular I/O's, etc. I.e., don't design for an "open" network onto which random, uncontrolled entities can be added at will. Instead, use it as a "really long bus" with devices having known characteristics along it's length. Just like when you design a system KNOWING which PCI cards will be inside! OTOH, you don't want to have to have a bunch of different mechanisms to access different devices *on* that bus! I.e., memory is memory regardless of whether its a buffer being used on a SCSI controller or an mbuf in the kernel!
> I had to deal with an extreme example of this in a component SRT > system I worked on back before high speed Internet was common. The > system was designed for use on a LAN, but the components were > separable and sometimes were used that way. The system could be run > successfully over dial-up lines [but it had to *know* which of its > components were on dial-up]. > > The problem was, at that time, businesses often interconnected LANs > using dial-up bridge routers: an apparently local LAN address could, > in fact, be in another city! [Or even another state!!] Many of these > bridges could be configured to ignore keep-alive packets and hang up > if you weren't sending real data. The next real message then would be > delayed - sometimes for many seconds - while the dial-up link was > reestablished. To save on phone bills, IT managers tended to > configure their bridges to shut down phone links quickly. > > The story is long and I won't bore you with it ... suffice to say that > things became a lot more complicated having to deal with the potential > for a customer to configure the system for a LAN and then try to run > it distributed over dial-up bridges. That caused me [and others] an > awful lot of headaches.
If your system had a *requirement* for a particular transit time, then its up to the implementors to guarantee that. You can't expect me to spool real time HD video to a 360K floppy disk! :> OTOH, you don't have to record video on *tape* (historically)! Avail yourself of whatever new technologies offer -- knowing what *new* capabilities this affords AND new limitations! I.e., splicing acetate film was a lot easier than digital video! All you needed was a sharp knife and some tape -- no high speed computers and fancy software... [C is ready. Off we go! :> ]
Reply by George Neuner November 21, 20132013-11-21
Hi Don,

On Tue, 19 Nov 2013 16:49:10 -0700, Don Y <this@isnotme.com> wrote:

>OK. But, did the *kernel* make the decision as to whether to >pass the message (IDL) on to the service (encoded in the >ticket) based on an examination of that field? Or, did the >kernel pass it on to the service and rely on the *server* to >decide "You aren't the right person"? (in addition to >"You don't have the right permission(s)"?)
Yes and no. Remember, to make ANY system call requires a ticket. Normally, a process is created with a set of default tickets that enable it to access basic services (filesystem, network, etc.). The default tickets are held by the kernel and can be chosen by the parent when the process is created. The default ticket for any particular call is assumed unless the process explicitly presents a different one when the call is made. However, the kernel IPC mechanism doesn't validate the message other than to check that the server address on the ticket is good. E.g., if the process has no ticket for the socket service,, it will be prevented from even trying to contact the socket service.
>A [Mach] "send once" right is a send right that is automatically >destroyed by the kernel when used. I.e., send *one* message, >no more.
You can do similar with numbered use tickets/capabilities in Amoeba. Everything you can think of has an implementation analogue in either system. You're just hung up on kernel enforcement. E.g., you could design an Amoeba based system where all the system capabilities were held by [or accessible to] a "validation server" to which all client calls are directed. The validation server would authenticate the client's ticket and then hand off the "connection" to the appropriate server.
>Permissions aren't a "bit set" that >you can add to or subtract from. I.e., there's no limit to >the number of individual permissions implemented by a single >"Handle". Nor do the permissions associated with one Handle >have to be the same as the set supported for some other Handle >(of the same object type).
Permissions in Amoeba aren't a "bit set" either ... unless you want them to be. You have a 32 bit field in the ticket to use however you want. The field can represent a bit map, or an enumerated value, or be divided into multiple fields. If you don't need to validate ticket holders, you've got another 96 bits in the v2 user id and 2nd signature field to play with. There is no *practical* limit to the set of privileges an Amoeba capability can administer or that a ticket can claim.
>> [Same model as Amoeba: first connect, then accept or reject.] > >The NORMA server made ports transparent. You didn't know >whether an IDL was going to result in an "IPC" or an "RPC". >You just "talked to a port for which you had send right(s)" >and the kernel did the rest. > >No separate permission required to use the network.
That's not my point. To do much of anything, a Mach server has to register a public service port with send rights - which any task in the network can scan for and try to connect to. Aside from limiting the count of available send rights to the port, there is no way to prevent anyone from connecting to it. Only *after* determining that the connection is unwanted can the server decide what to do about it. Using MIG didn't affect this: you couldn't encode port ids into the client using IDL - the only way for a client to find the server was to look up its registered public port.
>>> The only way for any of us to know a ticket (capability) has >>> been revoked is to try to *use* it. If I use mine or you >>> use yours, we get told THAT capability has been revoked. >>> But these other (nameless) two guys in my example have no idea >>> that this has happened (unless one of us knows to tell BOTH >>> of them). >> >> They get told when they try to use it. >> >> That's an administrative issue which has little to do with mechanism. >> In your system, revoked ports just evaporate leaving clients equally >> clueless unless *somebody* deigns to tell them why. > >No. There are two port-related "notification mechanisms" in Mach >(i.e., concerned with things that happen in the normal course of >a port's life). > >When the last "send right" *to* a particular port dies (is destroyed, >etc.), a "No More Senders" notification is delivered to the task >holding the receive right. I.e., no one is interested in the >object that you represent any longer. THERE IS NO WAY FOR ANYONE >TO REFERENCE IT (other than you!). Please feel free to deinstantiate >it and GC it's resources.
That tells the server all existing clients have disconnected ... it does NOT tell new clients they can't connect.
>When the receive right to a port dies (i.e., when the server that >is handling the incoming messages SENDed to that port dies or >destroys the port), a "Dead Name" notification is sent to holders >of send rights. I.e., the object that you were referencing >no longer exists. Don't bother trying to access it!
This tells the server or client the other side has disconnected. It does not tell them why.
>> Your clients don't know their port privileges have been revoked until >> they try to use their handles. Someone could be sitting on a useless >> handle for hours, days or weeks (how long since last reboot?). How is >> that *substantively* different from a cached ticket in Amoeba? > >Above.
You assume that both tasks [server and client] have implemented notify_server and do-mach-notify-<whatever>. Unless both sides are using MIG, you can't assume any notifications will be sent, received or handled - it isn't *required*. If a task doesn't implement the relevant notification handler, it won't know anything has happened until it gets an error trying to use the port.
>>> In mach, deleting the port (Handle) deletes all instances of it. >>> And, since the kernel knows where those instances are, those >>> Holders can be notified when that happens. >> >> Only if they were coded respond to the appropriate signal. And Mach >> didn't allow signaling a task on a remote host. > >If a task doesn't care to deal with "port death", it doesn't have >to. If it is important to the task's proper operation, it will!
You're assuming again.
>>> The "BSDSS" (Single Server). >> >> Was it really the SS version? > >Bad choice of names.
Ok. I misunderstood you to mean it was BSD's single user kernel - not simply Mach's "single server" model.
>But, I'm *reasonably* sure "systems" will be more physically >distributed. More processors. etc. And, forcing people to >use things like Berkeley sockets for all those comms is like >forcing a sprinter to wear scuba flippers! (Limbo/Inferno >was the final piece of the puzzle to fall into place. Many >of its ideas map naturally to this framework -- though with >an entirely different implementation! :< Now, I just have >to beat it all into submission! :> )
I really don't like network agnosticism. It simplifies coding for simple minded tasks, but it makes doing real time applications like audio/video streaming, and remote monitoring/control a b*tch because you don't have visibility to adjust to changing network conditions. I hate RPC for the same reason. I often use loopback connections to communicate between co-hosted tasks. At the very least, using a network API reminds me that the tasks may, in fact, be separated by a network. Sometimes it doesn't matter, but sometimes it does. I had to deal with an extreme example of this in a component SRT system I worked on back before high speed Internet was common. The system was designed for use on a LAN, but the components were separable and sometimes were used that way. The system could be run successfully over dial-up lines [but it had to *know* which of its components were on dial-up]. The problem was, at that time, businesses often interconnected LANs using dial-up bridge routers: an apparently local LAN address could, in fact, be in another city! [Or even another state!!] Many of these bridges could be configured to ignore keep-alive packets and hang up if you weren't sending real data. The next real message then would be delayed - sometimes for many seconds - while the dial-up link was reestablished. To save on phone bills, IT managers tended to configure their bridges to shut down phone links quickly. The story is long and I won't bore you with it ... suffice to say that things became a lot more complicated having to deal with the potential for a customer to configure the system for a LAN and then try to run it distributed over dial-up bridges. That caused me [and others] an awful lot of headaches. George
Reply by George Neuner November 21, 20132013-11-21
Hi Don,

On Tue, 19 Nov 2013 15:13:07 -0700, Don Y <this@isnotme.com> wrote:

>Actually, Amoeba *does* GC! Each server is responsible for >discarding "unreferenced" objects whenever a GC cycle is >invoked for/on that server. > >The difference is, Amoeba doesn't know where all the "references" >(handles/tickets) are. So, it has to, instead, implement GC >by noticing which objects haven't been *referenced* (actively >operated upon). > >So, a ticket holder has to periodically tickle the object for >which he's holding the ticket -- lest the server decide the >object is no longer being referenced ("of interest") to >anyone.
Not exactly.
>Amoeba had a "notice" (can't recall the name of the actual >method) operation for each object for this express purpose.
Amoeba's distributed filesystem is comprised of peer/proxy servers. When a non-local file is opened, the local server obtains a copy of the file's capability to accelerate future accesses. Then it acts as a proxy to the remote server, performing access validations locally and relaying requests/data over a trusted peer channel. The file servers time out and remove cached non-local capabilities if they aren't used. They do NOT time out capabilities for their own locally hosted objects. AFAIK, the filesystem is the only included peer/proxy service - all the others either are host local or are simply peer. I'm not aware that any of the other services time out capabilities. George