So you're doing a 'server push'? i.e. your fixed-IP hardware is behaving as a client, and dynamic IP machines are running ServiceHosts?
I really wish there was a 'smart layer' that one could simply lay in place that would deal with versioning here - but there really is no magic bullet.
The IExtensibleObject really is just to maintain data for round-tripping, but if the server only has the old contract, then it will also only have the old implementation, so it will not be able to 'use' this 'extended' information.
There are a couple of practices you could follow. The primary difference is, change version numbers, or dont... in the case of WCF contracts there is no version number as such, but the version is encoded in to the contract namespace/URI.
If you dont change the namespace, then you are basically saying, "I will have one service, that will NEVER break backwards compatibility (well, for X versions)". You must then implement a rigorous development practice to ensure that no methods are modified or removed, only additions can be made. This will prevent the client from sending requests that the server no longer understands.
The other approach is to change WCF contract URI/namespace with each revision of the contract. You 'server' will then have an implementation of EACH version of contract that you want to support. If, for example, you are supporting customers for 3 prior versions, they could be running the latest WCF server, but the WCF client must not be older than 3 versions, or the WCF server WILL not understand the request (because the URI/contract namespace will not exist).
This is usually the approach that I choose.
To do this, I have a tiered approach
WCF Contracts
This are highly static, only change when absolutely necessary
Each new contract receives a new namespace/URI
Contracts do NOT inherit from the previous version - doing so would prevent you from deprecating functionality.
WCF service implementation
This implemetation, or implementations, is just a SHIM layer - it receives the request and passes it along to appropriate logic layer.
Logic layer/adapters
if the request has arrived on the latest version contract, then it is immediately processed.
if the request has arrived on a prior version contract, an adapter is located for that version (this can actually happen in the service implementation if you like), and the request is passed to the adapter.
The adapters are responsible from receiving an old request, and adapting it to the namespace of the next newer version. This works in a chaining fashion, so, for example, if a request is received from a v1.0 client, to a v1.3 server
1.0 contract ->
1.0 to 1.1 adapter ->
1.1 to 1.2 adapter ->
1.2 to 1.3 adapter -> 1,3 implementation. (and back if not one-way)
Each adapter has the opportunity to perform prepare a request for processing by the next version.
For example, in a 1.1 method, you may have introduced the requirement for a 'request date' to be passed, but you know that you can 'make a best guess', so you add that argument in the 1.0 adapter before passing the request to the 1.1 logic.
Remember that it is still possible to introduce requirements that cannot be 'assumed' or 'defaulted' - these would be breaking contractual changes, and you would only want to do this when absolutely necessary. But because you havent chained your contracts, you can support both methods for a while, then drop the old contract from support, and all its old methods disappear.
Next, you also have the choice to separate your entities into another namespace that may be versioned independently from the logic contracts. Another kettle of fish.
You also need to decide how long you want to support old contracts - the longer you support them, the more complex your 'version handling' logic will get. The nice thing about chaining this logic in an implementation like the one above, is once you have written an adapter layer for a specific version, you dont ever have to revisit it.
Hopefully this has given you something to think about.